@defuse-protocol/nearintents-mpp-sdk 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +241 -0
  3. package/dist/Errors.d.ts +57 -0
  4. package/dist/Errors.d.ts.map +1 -0
  5. package/dist/Errors.js +54 -0
  6. package/dist/Errors.js.map +1 -0
  7. package/dist/Methods.d.ts +44 -0
  8. package/dist/Methods.d.ts.map +1 -0
  9. package/dist/Methods.js +23 -0
  10. package/dist/Methods.js.map +1 -0
  11. package/dist/Types.d.ts +125 -0
  12. package/dist/Types.d.ts.map +1 -0
  13. package/dist/Types.js +195 -0
  14. package/dist/Types.js.map +1 -0
  15. package/dist/client/Charge.d.ts +141 -0
  16. package/dist/client/Charge.d.ts.map +1 -0
  17. package/dist/client/Charge.js +167 -0
  18. package/dist/client/Charge.js.map +1 -0
  19. package/dist/client/index.d.ts +5 -0
  20. package/dist/client/index.d.ts.map +1 -0
  21. package/dist/client/index.js +5 -0
  22. package/dist/client/index.js.map +1 -0
  23. package/dist/index.d.ts +5 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +5 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/internal/OneClick.d.ts +243 -0
  28. package/dist/internal/OneClick.d.ts.map +1 -0
  29. package/dist/internal/OneClick.js +422 -0
  30. package/dist/internal/OneClick.js.map +1 -0
  31. package/dist/server/Charge.d.ts +241 -0
  32. package/dist/server/Charge.d.ts.map +1 -0
  33. package/dist/server/Charge.js +404 -0
  34. package/dist/server/Charge.js.map +1 -0
  35. package/dist/server/index.d.ts +5 -0
  36. package/dist/server/index.d.ts.map +1 -0
  37. package/dist/server/index.js +5 -0
  38. package/dist/server/index.js.map +1 -0
  39. package/package.json +87 -9
  40. package/src/Errors.ts +86 -0
  41. package/src/Methods.ts +24 -0
  42. package/src/Types.ts +256 -0
  43. package/src/client/Charge.ts +268 -0
  44. package/src/client/index.ts +4 -0
  45. package/src/index.ts +4 -0
  46. package/src/internal/OneClick.ts +634 -0
  47. package/src/server/Charge.ts +621 -0
  48. package/src/server/index.ts +4 -0
package/src/Types.ts ADDED
@@ -0,0 +1,256 @@
1
+ import { Receipt, z } from 'mppx'
2
+
3
+ /** Payment method name for `nearintents` challenges. */
4
+ export const paymentMethod = 'nearintents' as const
5
+
6
+ /** Payment intent name for one-time cross-chain charges. */
7
+ export const chargeIntent = 'charge' as const
8
+
9
+ /** `methodDetails.settlementBackend` disclosure value for this method. */
10
+ export const settlementBackend = 'near-intents' as const
11
+
12
+ /** Credential types supported by this method (spec: the only valid value is "hash"). */
13
+ export const credentialTypes = ['hash'] as const
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // CAIP-2 / CAIP-19 identifiers
17
+ //
18
+ // The wire format uses CAIP-19 for `currency` and `methodDetails.destinationAsset`
19
+ // and CAIP-2 for `methodDetails.originNetwork` / `methodDetails.destinationNetwork`.
20
+ // Identifiers MUST be compared by parsed components, with the asset reference
21
+ // compared in the chain's canonical form (EVM addresses case-insensitively).
22
+ // ---------------------------------------------------------------------------
23
+
24
+ const caip2Regex = /^([-a-z0-9]{3,8}):([-_a-zA-Z0-9]{1,32})$/
25
+ const caip19Regex =
26
+ /^([-a-z0-9]{3,8}):([-_a-zA-Z0-9]{1,32})\/([-a-z0-9]{3,8}):([-.%a-zA-Z0-9]{1,128})$/
27
+
28
+ /** Parsed CAIP-2 chain identifier. */
29
+ export type Caip2 = {
30
+ namespace: string
31
+ reference: string
32
+ }
33
+
34
+ /** Parsed CAIP-19 asset identifier. */
35
+ export type Caip19 = {
36
+ chain: Caip2
37
+ assetNamespace: string
38
+ assetReference: string
39
+ }
40
+
41
+ /** Parses a CAIP-2 chain identifier (e.g. `eip155:42161`). Throws on invalid input. */
42
+ export function parseCaip2(id: string): Caip2 {
43
+ const match = caip2Regex.exec(id)
44
+ if (!match) throw new Error(`Invalid CAIP-2 chain identifier: "${id}".`)
45
+ return { namespace: match[1]!, reference: match[2]! }
46
+ }
47
+
48
+ /** Parses a CAIP-19 asset identifier (e.g. `eip155:42161/erc20:0xaf88…`). Throws on invalid input. */
49
+ export function parseCaip19(id: string): Caip19 {
50
+ const match = caip19Regex.exec(id)
51
+ if (!match) throw new Error(`Invalid CAIP-19 asset identifier: "${id}".`)
52
+ return {
53
+ chain: { namespace: match[1]!, reference: match[2]! },
54
+ assetNamespace: match[3]!,
55
+ assetReference: match[4]!,
56
+ }
57
+ }
58
+
59
+ /** Returns the CAIP-2 chain component of a CAIP-19 asset identifier. */
60
+ export function chainOf(assetId: string): string {
61
+ const { chain } = parseCaip19(assetId)
62
+ return `${chain.namespace}:${chain.reference}`
63
+ }
64
+
65
+ /**
66
+ * Non-throwing {@link chainOf} for schema refines: zod runs object-level
67
+ * checks even when a field-level check already failed, so a malformed
68
+ * CAIP-19 must yield `false` (a normal validation issue), not an exception.
69
+ */
70
+ function chainOfSafe(assetId: string): string | undefined {
71
+ try {
72
+ return chainOf(assetId)
73
+ } catch {
74
+ return undefined
75
+ }
76
+ }
77
+
78
+ /** Compares two CAIP-2 identifiers by parsed components. */
79
+ export function networkEqual(a: string, b: string): boolean {
80
+ let left: Caip2
81
+ let right: Caip2
82
+ try {
83
+ left = parseCaip2(a)
84
+ right = parseCaip2(b)
85
+ } catch {
86
+ return false
87
+ }
88
+ return left.namespace === right.namespace && left.reference === right.reference
89
+ }
90
+
91
+ /**
92
+ * Compares two CAIP-19 identifiers by parsed components.
93
+ *
94
+ * The asset reference is compared in the chain's canonical form: for `eip155`
95
+ * chains (hex addresses) the comparison is case-insensitive; for all other
96
+ * chains it is exact (e.g. base58 references are case-sensitive).
97
+ */
98
+ export function assetEqual(a: string, b: string): boolean {
99
+ let left: Caip19
100
+ let right: Caip19
101
+ try {
102
+ left = parseCaip19(a)
103
+ right = parseCaip19(b)
104
+ } catch {
105
+ return false
106
+ }
107
+ if (left.chain.namespace !== right.chain.namespace) return false
108
+ if (left.chain.reference !== right.chain.reference) return false
109
+ if (left.assetNamespace !== right.assetNamespace) return false
110
+ if (left.chain.namespace === 'eip155')
111
+ return left.assetReference.toLowerCase() === right.assetReference.toLowerCase()
112
+ return left.assetReference === right.assetReference
113
+ }
114
+
115
+ /** Formats a payer identity as a `did:pkh` source (CAIP-2 network + payer address). */
116
+ export function toSource(parameters: { network: string; address: string }): string {
117
+ return `did:pkh:${parameters.network}:${parameters.address}`
118
+ }
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Schemas
122
+ // ---------------------------------------------------------------------------
123
+
124
+ /** Base-unit integer amount (e.g. "1000000" for 1 USDC at 6 decimals). */
125
+ export function atomicAmount() {
126
+ return z.string().check(z.regex(/^\d+$/, 'Invalid base-unit amount'))
127
+ }
128
+
129
+ /** CAIP-2 chain identifier string. */
130
+ export function caip2() {
131
+ return z.string().check(z.regex(caip2Regex, 'Invalid CAIP-2 chain identifier'))
132
+ }
133
+
134
+ /** CAIP-19 asset identifier string. */
135
+ export function caip19() {
136
+ return z.string().check(z.regex(caip19Regex, 'Invalid CAIP-19 asset identifier'))
137
+ }
138
+
139
+ /** NEAR Intents-specific `methodDetails` for the charge request (spec §Method Details). */
140
+ export const MethodDetailsSchema = z.object({
141
+ /** CAIP-2 identifier of the origin chain (where `recipient` lives and the deposit tx is anchored). */
142
+ originNetwork: caip2(),
143
+ /** CAIP-2 identifier of the chain where the merchant receives the destination asset. */
144
+ destinationNetwork: caip2(),
145
+ /** Destination asset the merchant receives, as CAIP-19; chain component MUST equal `destinationNetwork`. */
146
+ destinationAsset: caip19(),
147
+ /** Merchant address on the destination chain that receives `amountOut`. */
148
+ destinationRecipient: z.string().check(z.minLength(1)),
149
+ /** Exact amount the merchant receives, in base units of `destinationAsset` (EXACT_OUTPUT). */
150
+ amountOut: atomicAmount(),
151
+ /** Minimum deposit the backend accepts, in base units of `currency` — the verification threshold. */
152
+ minAmountIn: atomicAmount(),
153
+ /** Deposit memo required by some origin chains (e.g. Stellar). Absent or null when not required. */
154
+ depositMemo: z.optional(z.nullable(z.string())),
155
+ /** Slippage tolerance in basis points (e.g. 100 = 1%). */
156
+ slippageTolerance: z.optional(z.number()),
157
+ /** Estimated swap completion time in seconds, from the 1Click quote. */
158
+ timeEstimate: z.optional(z.number()),
159
+ /** Origin-chain address refunded if the swap fails or excess exists. */
160
+ refundTo: z.string().check(z.minLength(1)),
161
+ /** Settlement backend disclosure. Always "near-intents" for this method. */
162
+ settlementBackend: z.optional(z.literal(settlementBackend)),
163
+ /** Ordered list of accepted credential types. Only "hash" is valid for this method. */
164
+ credentialTypes: z.optional(z.array(z.literal('hash')).check(z.minLength(1))),
165
+ })
166
+ export type MethodDetails = z.infer<typeof MethodDetailsSchema>
167
+
168
+ /**
169
+ * Canonical `nearintents` charge request (the decoded challenge `request` parameter).
170
+ *
171
+ * The standard fields describe the payment the client makes on the origin
172
+ * chain (`recipient` is the single-use 1Click deposit address); the merchant's
173
+ * destination leg is carried in `methodDetails`.
174
+ */
175
+ export const ChargeRequestSchema = z
176
+ .object({
177
+ /**
178
+ * Deposit amount the client is asked to send, in base units of `currency`:
179
+ * the quote's maximum input that guarantees the merchant receives
180
+ * `methodDetails.amountOut` (the spec calls this `maxAmountIn`; the 1Click
181
+ * quote response field is `amountIn`).
182
+ */
183
+ amount: atomicAmount(),
184
+ /** Source asset the client pays with, as CAIP-19; chain component MUST equal `methodDetails.originNetwork`. */
185
+ currency: caip19(),
186
+ /** 1Click deposit address on the origin chain — the payee of the client's on-chain transfer. */
187
+ recipient: z.string().check(z.minLength(1)),
188
+ /** Human-readable payment description. MUST NOT be relied upon for verification. */
189
+ description: z.optional(z.string()),
190
+ /** Merchant reference (order ID, invoice number, etc.). */
191
+ externalId: z.optional(z.string()),
192
+ methodDetails: MethodDetailsSchema,
193
+ })
194
+ .check(
195
+ z.refine((request) => {
196
+ const chain = chainOfSafe(request.currency)
197
+ return chain !== undefined && networkEqual(chain, request.methodDetails.originNetwork)
198
+ }, 'currency chain component must equal methodDetails.originNetwork'),
199
+ z.refine((request) => {
200
+ const chain = chainOfSafe(request.methodDetails.destinationAsset)
201
+ return chain !== undefined && networkEqual(chain, request.methodDetails.destinationNetwork)
202
+ }, 'destinationAsset chain component must equal methodDetails.destinationNetwork'),
203
+ )
204
+ export type ChargeRequest = z.infer<typeof ChargeRequestSchema>
205
+
206
+ /** Push-mode credential payload: the confirmed origin-chain deposit transaction hash. */
207
+ export const HashPayloadSchema = z.object({
208
+ type: z.literal('hash'),
209
+ /** Transaction hash of the client's deposit on `methodDetails.originNetwork` (chain-native format). */
210
+ hash: z.string().check(z.minLength(1)),
211
+ })
212
+ export type HashPayload = z.infer<typeof HashPayloadSchema>
213
+
214
+ // ---------------------------------------------------------------------------
215
+ // Receipt
216
+ // ---------------------------------------------------------------------------
217
+
218
+ /**
219
+ * `nearintents` Payment-Receipt payload (spec §Receipt).
220
+ *
221
+ * Extends the mppx base receipt with the method's REQUIRED `challengeId` and
222
+ * `originTxHash` fields (and optional `destinationNetwork`). `reference` is
223
+ * the destination-chain transaction hash of the merchant delivery.
224
+ */
225
+ export type NearIntentsReceipt = Receipt.Receipt & {
226
+ method: typeof paymentMethod
227
+ /** The `id` from the original challenge. */
228
+ challengeId: string
229
+ /** The client's origin-chain deposit transaction hash (`payload.hash`). */
230
+ originTxHash: string
231
+ /** CAIP-2 identifier of the chain where the merchant was paid. */
232
+ destinationNetwork?: string | undefined
233
+ }
234
+
235
+ /** Builds a spec-conformant `nearintents` receipt through `Receipt.from`. */
236
+ export function toReceipt(parameters: {
237
+ challengeId: string
238
+ /** Destination-chain transaction hash of the merchant delivery. */
239
+ reference: string
240
+ originTxHash: string
241
+ destinationNetwork?: string | undefined
242
+ externalId?: string | undefined
243
+ timestamp?: string | undefined
244
+ }): NearIntentsReceipt {
245
+ const { challengeId, destinationNetwork, externalId, originTxHash, reference } = parameters
246
+ return Receipt.from({
247
+ method: paymentMethod,
248
+ reference,
249
+ status: 'success',
250
+ timestamp: parameters.timestamp ?? new Date().toISOString(),
251
+ challengeId,
252
+ originTxHash,
253
+ ...(destinationNetwork !== undefined && { destinationNetwork }),
254
+ ...(externalId !== undefined && { externalId }),
255
+ }) as NearIntentsReceipt
256
+ }
@@ -0,0 +1,268 @@
1
+ import { Credential, Errors, Expires, Method, z } from 'mppx'
2
+
3
+ import * as Methods from '../Methods.js'
4
+ import * as Types from '../Types.js'
5
+
6
+ /**
7
+ * Creates a `nearintents` charge method for usage on the client.
8
+ *
9
+ * The client pays before delivery, so the spec's client-side MUSTs are
10
+ * enforced as assertions: the challenge request is schema-validated, `expires`
11
+ * is refused when passed, and the configured payment policy (allowed origin
12
+ * networks/assets, per-asset `maxAmountIn` caps, expected destination leg) is
13
+ * checked before any deposit is made.
14
+ *
15
+ * The deposit itself is provided one of three ways (first match wins):
16
+ * 1. `context.hash` — the deposit was already broadcast; present its tx hash.
17
+ * 2. `sendDeposit` — a callback that pays the origin-chain leg and resolves
18
+ * with the confirmed tx hash (bring-your-own-chain).
19
+ * 3. `walletClient` (config or context) — built-in EVM broadcast for
20
+ * `eip155:*` origins (native transfers and ERC-20 `transfer`); any viem
21
+ * WalletClient extended with public actions satisfies the interface.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * import { Mppx } from 'mppx/client'
26
+ * import { nearintents } from '@defuse-protocol/nearintents-mpp-sdk/client'
27
+ *
28
+ * const mppx = Mppx.create({
29
+ * methods: [
30
+ * nearintents.charge({
31
+ * walletClient,
32
+ * policy: {
33
+ * allowedOriginNetworks: ['eip155:42161'],
34
+ * maxAmountIn: { 'eip155:42161/erc20:0xaf88…5831': '5000000' },
35
+ * },
36
+ * }),
37
+ * ],
38
+ * })
39
+ * const response = await mppx.fetch('https://api.example.com/paid-resource')
40
+ * ```
41
+ */
42
+ export function charge(parameters: charge.Parameters = {}) {
43
+ return Method.toClient(Methods.charge, {
44
+ context: z.object({
45
+ /** Tx hash of an already-broadcast deposit (push mode, bring-your-own-broadcast). */
46
+ hash: z.optional(z.string()),
47
+ /** Per-request EVM wallet client (overrides the configured one). */
48
+ walletClient: z.optional(z.custom<charge.EvmWalletClient>()),
49
+ }),
50
+
51
+ canHandleChallenge({ challenge }) {
52
+ if (challenge.method !== Types.paymentMethod || challenge.intent !== Types.chargeIntent)
53
+ return false
54
+ const parsed = Types.ChargeRequestSchema.safeParse(challenge.request)
55
+ if (!parsed.success) return false
56
+ try {
57
+ assertPolicy(parameters.policy, parsed.data)
58
+ return true
59
+ } catch {
60
+ return false
61
+ }
62
+ },
63
+
64
+ async createCredential({ challenge, context }) {
65
+ // Spec §Amount and Asset Verification: decode and verify the challenge
66
+ // request before paying; never rely on `description`.
67
+ const request = Types.ChargeRequestSchema.parse(challenge.request)
68
+ // Spec §Expiry: MUST NOT broadcast a deposit after `expires`.
69
+ Expires.assert(challenge.expires, challenge.id)
70
+ assertPolicy(parameters.policy, request)
71
+
72
+ const walletClient = context?.walletClient ?? parameters.walletClient
73
+ const hash = await (async () => {
74
+ if (context?.hash) return context.hash
75
+ if (parameters.sendDeposit) return parameters.sendDeposit({ challenge, request })
76
+ if (walletClient) return broadcastEvmDeposit(walletClient, request)
77
+ throw new Error(
78
+ 'nearintents-mpp-sdk: no way to pay — provide context.hash, a sendDeposit callback, or a walletClient.',
79
+ )
80
+ })()
81
+
82
+ const source =
83
+ parameters.source ??
84
+ (walletClient?.account?.address
85
+ ? Types.toSource({
86
+ network: request.methodDetails.originNetwork,
87
+ address: walletClient.account.address,
88
+ })
89
+ : undefined)
90
+
91
+ return Credential.serialize(
92
+ Credential.from({
93
+ challenge,
94
+ payload: { type: 'hash', hash },
95
+ ...(source && { source }),
96
+ }),
97
+ )
98
+ },
99
+ })
100
+ }
101
+
102
+ export declare namespace charge {
103
+ /**
104
+ * Payment policy — the client's safety surface (it pays before delivery).
105
+ * Every configured list is an allowlist; `maxAmountIn` entries double as a
106
+ * currency allowlist when present.
107
+ */
108
+ type Policy = {
109
+ /** CAIP-2 origin networks the client is willing to pay on. */
110
+ allowedOriginNetworks?: readonly string[] | undefined
111
+ /** CAIP-19 source assets the client is willing to pay with. */
112
+ allowedCurrencies?: readonly string[] | undefined
113
+ /**
114
+ * Per-asset deposit caps in base units, keyed by CAIP-19 id. When set,
115
+ * a challenge whose `currency` has no entry is refused.
116
+ */
117
+ maxAmountIn?: Record<string, string> | undefined
118
+ /** Expected merchant destination leg; mismatches are refused. */
119
+ expectedDestination?:
120
+ | {
121
+ destinationAsset?: string | undefined
122
+ destinationRecipient?: string | undefined
123
+ amountOut?: string | undefined
124
+ }
125
+ | undefined
126
+ }
127
+
128
+ /**
129
+ * Minimal EVM wallet surface for the built-in `eip155:*` deposit broadcast.
130
+ * A viem `WalletClient` created with `account` and `chain` (so both default
131
+ * per call) and extended with public actions satisfies it;
132
+ * `waitForTransactionReceipt` is optional but strongly recommended — the
133
+ * spec expects a *confirmed* deposit hash.
134
+ */
135
+ type EvmWalletClient = {
136
+ account?: { address: string } | undefined
137
+ chain?: { id: number } | undefined
138
+ sendTransaction: (args: {
139
+ to: `0x${string}`
140
+ value?: bigint | undefined
141
+ data?: `0x${string}` | undefined
142
+ }) => Promise<string>
143
+ waitForTransactionReceipt?:
144
+ | ((args: { hash: `0x${string}` }) => Promise<{ status?: string | undefined }>)
145
+ | undefined
146
+ }
147
+
148
+ type SendDeposit = (parameters: {
149
+ challenge: { id: string; expires?: string | undefined }
150
+ request: Types.ChargeRequest
151
+ }) => Promise<string>
152
+
153
+ type Parameters = {
154
+ /** Payment policy asserted before every deposit. */
155
+ policy?: Policy | undefined
156
+ /** Pays the origin-chain leg and resolves with the confirmed tx hash. */
157
+ sendDeposit?: SendDeposit | undefined
158
+ /** EVM wallet for the built-in `eip155:*` broadcast. */
159
+ walletClient?: EvmWalletClient | undefined
160
+ /** Payer DID for the credential `source` (defaults to did:pkh from the wallet). */
161
+ source?: string | undefined
162
+ }
163
+ }
164
+
165
+ /** @internal */
166
+ function assertPolicy(policy: charge.Policy | undefined, request: Types.ChargeRequest): void {
167
+ if (!policy) return
168
+ const { methodDetails } = request
169
+
170
+ if (
171
+ policy.allowedOriginNetworks &&
172
+ !policy.allowedOriginNetworks.some((network) =>
173
+ Types.networkEqual(network, methodDetails.originNetwork),
174
+ )
175
+ )
176
+ throw new PolicyError(`origin network ${methodDetails.originNetwork} is not allowed`)
177
+
178
+ if (
179
+ policy.allowedCurrencies &&
180
+ !policy.allowedCurrencies.some((asset) => Types.assetEqual(asset, request.currency))
181
+ )
182
+ throw new PolicyError(`currency ${request.currency} is not allowed`)
183
+
184
+ if (policy.maxAmountIn) {
185
+ const cap = Object.entries(policy.maxAmountIn).find(([asset]) =>
186
+ Types.assetEqual(asset, request.currency),
187
+ )?.[1]
188
+ if (cap === undefined)
189
+ throw new PolicyError(`currency ${request.currency} has no maxAmountIn entry`)
190
+ if (BigInt(request.amount) > BigInt(cap))
191
+ throw new PolicyError(`amount ${request.amount} exceeds the ${cap} cap for this asset`)
192
+ }
193
+
194
+ const expected = policy.expectedDestination
195
+ if (expected) {
196
+ if (
197
+ expected.destinationAsset !== undefined &&
198
+ !Types.assetEqual(expected.destinationAsset, methodDetails.destinationAsset)
199
+ )
200
+ throw new PolicyError('destination asset does not match the expected destination')
201
+ if (
202
+ expected.destinationRecipient !== undefined &&
203
+ expected.destinationRecipient !== methodDetails.destinationRecipient
204
+ )
205
+ throw new PolicyError('destination recipient does not match the expected destination')
206
+ if (expected.amountOut !== undefined && expected.amountOut !== methodDetails.amountOut)
207
+ throw new PolicyError('amountOut does not match the expected destination')
208
+ }
209
+ }
210
+
211
+ /** @internal Policy violations map to invalid-payload problems client-side. */
212
+ class PolicyError extends Errors.PaymentError {
213
+ override readonly name = 'PolicyError'
214
+ readonly title = 'Payment Policy Violation'
215
+ readonly type = 'https://paymentauth.org/problems/invalid-payload'
216
+
217
+ constructor(reason: string) {
218
+ super(`Payment policy violation: ${reason}.`)
219
+ }
220
+ }
221
+
222
+ /** @internal Broadcasts the deposit for `eip155:*` origins and returns the confirmed hash. */
223
+ async function broadcastEvmDeposit(
224
+ walletClient: charge.EvmWalletClient,
225
+ request: Types.ChargeRequest,
226
+ ): Promise<string> {
227
+ const currency = Types.parseCaip19(request.currency)
228
+ if (currency.chain.namespace !== 'eip155')
229
+ throw new Error(
230
+ `nearintents-mpp-sdk: the built-in wallet broadcast only supports eip155 origins (got ${request.methodDetails.originNetwork}); use sendDeposit or context.hash.`,
231
+ )
232
+ if (request.methodDetails.depositMemo != null)
233
+ throw new Error('nearintents-mpp-sdk: EVM deposits cannot carry a deposit memo.')
234
+
235
+ const expectedChainId = Number(currency.chain.reference)
236
+ if (walletClient.chain?.id !== expectedChainId)
237
+ throw new Error(
238
+ `nearintents-mpp-sdk: wallet is on chain ${walletClient.chain?.id ?? 'unknown'} but the challenge origin is eip155:${expectedChainId}.`,
239
+ )
240
+
241
+ const amount = BigInt(request.amount)
242
+ const recipient = request.recipient as `0x${string}`
243
+
244
+ // account/chain are intentionally omitted — the wallet's own defaults apply
245
+ // (and the chain-id assertion above already pinned the network).
246
+ const hash = await (async () => {
247
+ if (currency.assetNamespace === 'slip44')
248
+ return walletClient.sendTransaction({ to: recipient, value: amount })
249
+ // ERC-20 transfer(address,uint256) calldata — 0xa9059cbb + padded args.
250
+ const data =
251
+ `0xa9059cbb${recipient.slice(2).toLowerCase().padStart(64, '0')}${amount.toString(16).padStart(64, '0')}` as `0x${string}`
252
+ return walletClient.sendTransaction({
253
+ to: currency.assetReference as `0x${string}`,
254
+ data,
255
+ })
256
+ })()
257
+
258
+ // Present a *confirmed* hash when the client can wait for inclusion.
259
+ if (walletClient.waitForTransactionReceipt) {
260
+ const receipt = await walletClient.waitForTransactionReceipt({
261
+ hash: hash as `0x${string}`,
262
+ })
263
+ if (receipt.status && receipt.status !== 'success')
264
+ throw new Error(`nearintents-mpp-sdk: deposit transaction ${hash} reverted.`)
265
+ }
266
+
267
+ return hash
268
+ }
@@ -0,0 +1,4 @@
1
+ export * as Errors from '../Errors.js'
2
+ export * as Methods from '../Methods.js'
3
+ export * as Types from '../Types.js'
4
+ export * as nearintents from './Charge.js'
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * as Errors from './Errors.js'
2
+ export * as OneClick from './internal/OneClick.js'
3
+ export * as Methods from './Methods.js'
4
+ export * as Types from './Types.js'