@ar.io/sdk 4.0.0-solana.35 → 4.0.0-solana.36

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.
@@ -18,15 +18,18 @@
18
18
  *
19
19
  * Produces the EXACT bytes a recipient signs to release an escrowed
20
20
  * ANT. Output MUST be byte-identical to the Rust implementation in
21
- * `contracts/programs/ario-ant-escrow/src/canonical.rs::build_canonical_message`.
22
- * Cross-language equivalence is asserted by `canonical-message.test.ts`
23
- * which spawns the Rust `canonical` example binary and diffs the bytes.
21
+ * `ario-ant-escrow/src/canonical.rs::build_ant_escrow_claim_message`
22
+ * (header `ANT_ESCROW_CLAIM_HEADER = "ar.io ant-escrow claim"`). The on-chain
23
+ * program reconstructs these exact bytes and verifies the signature against
24
+ * them, so any drift (header, field set, or ordering) makes every claim fail
25
+ * `EthereumAddressMismatch` / signature verification.
24
26
  *
25
27
  * Format (UTF-8, line-feed separated, no trailing newline):
26
28
  *
27
29
  * ```text
28
- * ar.io ant-escrow claim v1
30
+ * ar.io ant-escrow claim
29
31
  * network: <network>
32
+ * recipient: <base64url(sha256(recipient_pubkey)) — 43 chars, no pad>
30
33
  * ant: <ant_mint_base58>
31
34
  * claimant: <claimant_solana_pubkey_base58>
32
35
  * nonce: <nonce_hex_lowercase>
@@ -37,8 +40,9 @@
37
40
  * - Ethereum: `wallet.signMessage(bytes)` → 65-byte ECDSA + EIP-191 sig
38
41
  * (the wallet applies the EIP-191 prefix; on-chain code re-applies it).
39
42
  */
40
- /** Header literal must match Rust `CANONICAL_HEADER`. */
41
- const CANONICAL_HEADER = 'ar.io ant-escrow claim v1';
43
+ import { sha256 } from '@noble/hashes/sha256';
44
+ /** Header literal must match Rust `ANT_ESCROW_CLAIM_HEADER`. */
45
+ const CANONICAL_HEADER = 'ar.io ant-escrow claim';
42
46
  /**
43
47
  * Build the canonical claim message bytes. UTF-8 encoded, no trailing
44
48
  * newline, exactly the format shown in the docstring.
@@ -52,21 +56,23 @@ export function canonicalMessage(input) {
52
56
  }
53
57
  const text = `${CANONICAL_HEADER}\n` +
54
58
  `network: ${input.network}\n` +
59
+ `recipient: ${deriveRecipientId(input.recipient)}\n` +
55
60
  `ant: ${input.antMint}\n` +
56
61
  `claimant: ${input.claimant}\n` +
57
62
  `nonce: ${bytesToHexLower(input.nonce)}`;
58
63
  return new TextEncoder().encode(text);
59
64
  }
60
- /** Header literal — must match Rust `CANONICAL_HEADER_V2`. */
61
- const CANONICAL_HEADER_V2 = 'ar.io escrow claim v2';
65
+ /** Header literal — must match Rust `ESCROW_CLAIM_HEADER`. */
66
+ const CANONICAL_HEADER_V2 = 'ar.io escrow claim';
62
67
  /**
63
68
  * Build the v2 canonical claim message bytes for token/vault escrows.
64
69
  * UTF-8 encoded, no trailing newline.
65
70
  *
66
71
  * Format:
67
72
  * ```text
68
- * ar.io escrow claim v2
73
+ * ar.io escrow claim
69
74
  * network: <network>
75
+ * recipient: <base64url(sha256(recipient_pubkey)) — 43 chars, no pad>
70
76
  * type: <token|vault>
71
77
  * asset: <asset_id_hex_lowercase_64chars>
72
78
  * amount: <u64_decimal>
@@ -85,6 +91,7 @@ export function canonicalMessageV2(input) {
85
91
  }
86
92
  const text = `${CANONICAL_HEADER_V2}\n` +
87
93
  `network: ${input.network}\n` +
94
+ `recipient: ${deriveRecipientId(input.recipient)}\n` +
88
95
  `type: ${input.assetType}\n` +
89
96
  `asset: ${bytesToHexLower(input.assetId)}\n` +
90
97
  `amount: ${input.amount.toString()}\n` +
@@ -95,6 +102,20 @@ export function canonicalMessageV2(input) {
95
102
  // =========================================
96
103
  // Shared utilities
97
104
  // =========================================
105
+ /**
106
+ * Recipient identity bound into the claim message — `base64url(sha256(bytes))`
107
+ * with no padding (32-byte hash → 43 chars). Byte-identical to the contract's
108
+ * `canonical.rs::derive_recipient_id_b64url`. Input is the recipient pubkey
109
+ * bytes the deposit targeted (ETH 20-byte address / Solana 32-byte pubkey /
110
+ * Arweave RSA modulus, etc.).
111
+ */
112
+ export function deriveRecipientId(recipient) {
113
+ if (recipient.length === 0) {
114
+ throw new Error('deriveRecipientId: recipient bytes must be non-empty');
115
+ }
116
+ // Node's 'base64url' encoding is unpadded — matches the Rust no-pad alphabet.
117
+ return Buffer.from(sha256(recipient)).toString('base64url');
118
+ }
98
119
  /** Lowercase-hex encoding. Matches Rust `encode_hex_lowercase`. */
99
120
  export function bytesToHexLower(bytes) {
100
121
  let s = '';
@@ -91,7 +91,7 @@ export { ANTEscrow, TokenEscrow,
91
91
  // in lock-step so users never see a raw on-chain error.
92
92
  assertVaultClaimable, isVaultClaimable, CLOCK_SKEW_TOLERANCE_SECONDS, } from './escrow.js';
93
93
  // Canonical claim-message helper (byte-equivalent to Rust impl)
94
- export { canonicalMessage, canonicalMessageV2, bytesToHexLower, } from './canonical-message.js';
94
+ export { canonicalMessage, canonicalMessageV2, deriveRecipientId, bytesToHexLower, } from './canonical-message.js';
95
95
  // ANT spawn (mint MPL Core asset + initialize ario-ant state in one tx)
96
96
  export { spawnSolanaANT, ARIO_LOGO_TX_ID, DEFAULT_ANT_TRANSACTION_ID, } from './spawn-ant.js';
97
97
  // PDA derivation
@@ -14,4 +14,4 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH
17
- export const version = '4.0.0-solana.35';
17
+ export const version = '4.0.0-solana.36';
@@ -18,15 +18,18 @@
18
18
  *
19
19
  * Produces the EXACT bytes a recipient signs to release an escrowed
20
20
  * ANT. Output MUST be byte-identical to the Rust implementation in
21
- * `contracts/programs/ario-ant-escrow/src/canonical.rs::build_canonical_message`.
22
- * Cross-language equivalence is asserted by `canonical-message.test.ts`
23
- * which spawns the Rust `canonical` example binary and diffs the bytes.
21
+ * `ario-ant-escrow/src/canonical.rs::build_ant_escrow_claim_message`
22
+ * (header `ANT_ESCROW_CLAIM_HEADER = "ar.io ant-escrow claim"`). The on-chain
23
+ * program reconstructs these exact bytes and verifies the signature against
24
+ * them, so any drift (header, field set, or ordering) makes every claim fail
25
+ * `EthereumAddressMismatch` / signature verification.
24
26
  *
25
27
  * Format (UTF-8, line-feed separated, no trailing newline):
26
28
  *
27
29
  * ```text
28
- * ar.io ant-escrow claim v1
30
+ * ar.io ant-escrow claim
29
31
  * network: <network>
32
+ * recipient: <base64url(sha256(recipient_pubkey)) — 43 chars, no pad>
30
33
  * ant: <ant_mint_base58>
31
34
  * claimant: <claimant_solana_pubkey_base58>
32
35
  * nonce: <nonce_hex_lowercase>
@@ -50,6 +53,12 @@ export interface CanonicalMessageInput {
50
53
  /** Solana pubkey that will receive the ANT on claim. Bound into the
51
54
  * signature so front-runners can't redirect. */
52
55
  claimant: Address;
56
+ /** Recipient identity pubkey bytes the deposit targeted (ETH 20-byte
57
+ * address, Solana 32-byte pubkey, or Arweave RSA modulus). Bound into the
58
+ * message as `recipient: base64url(sha256(bytes))` to match the contract's
59
+ * `derive_recipient_id_b64url`. REQUIRED — the program reconstructs this
60
+ * line, so omitting it makes the claim mismatch. */
61
+ recipient: Uint8Array;
53
62
  /** 32-byte anti-replay nonce — read from the EscrowAnt account. */
54
63
  nonce: Uint8Array;
55
64
  }
@@ -72,6 +81,12 @@ export interface CanonicalMessageV2Input {
72
81
  amount: bigint;
73
82
  /** Solana pubkey that will receive the tokens on claim. */
74
83
  claimant: Address;
84
+ /** Recipient identity pubkey bytes the deposit targeted (ETH 20-byte
85
+ * address, Solana 32-byte pubkey, or Arweave RSA modulus). Bound into the
86
+ * message as `recipient: base64url(sha256(bytes))` to match the contract's
87
+ * `derive_recipient_id_b64url`. REQUIRED — the program reconstructs this
88
+ * line, so omitting it makes the claim mismatch. */
89
+ recipient: Uint8Array;
75
90
  /** 32-byte anti-replay nonce — read from the EscrowToken account. */
76
91
  nonce: Uint8Array;
77
92
  }
@@ -81,8 +96,9 @@ export interface CanonicalMessageV2Input {
81
96
  *
82
97
  * Format:
83
98
  * ```text
84
- * ar.io escrow claim v2
99
+ * ar.io escrow claim
85
100
  * network: <network>
101
+ * recipient: <base64url(sha256(recipient_pubkey)) — 43 chars, no pad>
86
102
  * type: <token|vault>
87
103
  * asset: <asset_id_hex_lowercase_64chars>
88
104
  * amount: <u64_decimal>
@@ -93,5 +109,13 @@ export interface CanonicalMessageV2Input {
93
109
  * @throws if `assetId` or `nonce` aren't exactly 32 bytes.
94
110
  */
95
111
  export declare function canonicalMessageV2(input: CanonicalMessageV2Input): Uint8Array;
112
+ /**
113
+ * Recipient identity bound into the claim message — `base64url(sha256(bytes))`
114
+ * with no padding (32-byte hash → 43 chars). Byte-identical to the contract's
115
+ * `canonical.rs::derive_recipient_id_b64url`. Input is the recipient pubkey
116
+ * bytes the deposit targeted (ETH 20-byte address / Solana 32-byte pubkey /
117
+ * Arweave RSA modulus, etc.).
118
+ */
119
+ export declare function deriveRecipientId(recipient: Uint8Array): string;
96
120
  /** Lowercase-hex encoding. Matches Rust `encode_hex_lowercase`. */
97
121
  export declare function bytesToHexLower(bytes: Uint8Array): string;
@@ -68,7 +68,7 @@ export type { SolanaANTRegistryWriteableConfig } from './ant-registry-writeable.
68
68
  export type { AclMaintenanceOp, AclMaintenanceRole, } from '../types/ant-registry.js';
69
69
  export { ANTEscrow, TokenEscrow, assertVaultClaimable, isVaultClaimable, CLOCK_SKEW_TOLERANCE_SECONDS, } from './escrow.js';
70
70
  export type { ANTEscrowConfig, EscrowAntState, EscrowAssetType, EscrowProtocol, EscrowTokenState, } from './escrow.js';
71
- export { canonicalMessage, canonicalMessageV2, bytesToHexLower, } from './canonical-message.js';
71
+ export { canonicalMessage, canonicalMessageV2, deriveRecipientId, bytesToHexLower, } from './canonical-message.js';
72
72
  export type { CanonicalMessageInput, CanonicalMessageV2Input, EscrowNetwork, } from './canonical-message.js';
73
73
  export { spawnSolanaANT, ARIO_LOGO_TX_ID, DEFAULT_ANT_TRANSACTION_ID, } from './spawn-ant.js';
74
74
  export type { SpawnSolanaANTParams, SpawnSolanaANTResult, SpawnSolanaANTState, } from './spawn-ant.js';
@@ -13,4 +13,4 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- export declare const version = "4.0.0-solana.34";
16
+ export declare const version = "4.0.0-solana.35";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ar.io/sdk",
3
- "version": "4.0.0-solana.35",
3
+ "version": "4.0.0-solana.36",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ar-io/ar-io-sdk.git"
@@ -115,6 +115,7 @@
115
115
  },
116
116
  "dependencies": {
117
117
  "@ar.io/solana-contracts": "0.7.0-staging.17",
118
+ "@noble/hashes": "^1.8.0",
118
119
  "@solana-program/address-lookup-table": "^0.11.0",
119
120
  "@solana-program/compute-budget": "^0.15.0",
120
121
  "@solana-program/token": "^0.13.0",