Skip to content

Commit 7f73bf0

Browse files
committed
Update Starknet implementation for new contract
1 parent a17d8da commit 7f73bf0

6 files changed

Lines changed: 1051 additions & 805 deletions

File tree

‎apps/app/lib/rpc/nonEvmNodes.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export const NON_EVM_NODES: Record<string, NetworkNode[]> = {
2424
{ url: 'https://rpc.starknet.lava.build', providerName: 'lava' },
2525
],
2626
'starknet:SN_SEPOLIA': [
27-
{ url: 'https://starknet-sepolia.drpc.org', providerName: 'drpc' },
2827
{ url: 'https://starknet-sepolia-rpc.publicnode.com', providerName: 'publicnode' },
28+
{ url: 'https://starknet-sepolia.drpc.org', providerName: 'drpc' },
2929
],
3030

3131
// ── Aztec ──
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { describe, it, expect } from 'vitest'
2+
import { Contract, RpcProvider } from 'starknet'
3+
import abi from '../abis/STARKNET_HTLC.json' with { type: 'json' }
4+
import { buildUserLockTx } from '../client/wallet/buildUserLockTx'
5+
import { buildRefundTx } from '../client/wallet/buildRefundTx'
6+
import { buildRedeemSolverTx } from '../client/wallet/buildRedeemSolverTx'
7+
import { ZERO_ADDRESS } from '../constants'
8+
9+
// Deployed Train contract (Starknet Sepolia). Only used offline to build an ABI-aware
10+
// Contract for `populate`, which is the source of truth for calldata ordering.
11+
const CONTRACT = '0x4ae1ae0dd1dd01be306725ab8de15707241284c8dd49cbaf37bcf845bf2d20b'
12+
const USER = '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
13+
const SOLVER = '0x00abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef01'
14+
const TOKEN = '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d'
15+
16+
const contract = () =>
17+
new Contract({ abi: abi as any, address: CONTRACT, providerOrAccount: new RpcProvider({ nodeUrl: 'http://offline' }) })
18+
const felts = (cd: unknown) => (cd as string[]).map(v => BigInt(v))
19+
20+
const baseUserLockParams: any = {
21+
sourceChain: 'starknet-sepolia', destinationChain: 'ethereum-sepolia',
22+
amount: '1.5', destinationAmount: '1000000',
23+
sourceAsset: { contract: TOKEN, decimals: 18 },
24+
destinationAsset: { contract: '0x0000000000000000000000000000000000000000' },
25+
srcSolverAddress: SOLVER, atomicContract: CONTRACT, sourceAddress: USER,
26+
destinationAddress: '0x0000000000000000000000000000000000000001',
27+
payoutCurve: '', quoteExpiry: 1899999999,
28+
rewardToken: '', rewardRecipient: '', rewardTimelockDelta: 0,
29+
timelockDelta: 3600, hashlock: '0x1234abcd', nonce: 1721600000000,
30+
}
31+
32+
describe('Starknet buildUserLockTx', () => {
33+
it('serializes UserLockParams in the exact new contract order (no sender; refund_to/payout_curve/payout_curve_data)', () => {
34+
const built = felts(buildUserLockTx(baseUserLockParams).calldata)
35+
// ABI-driven reference: pass raw values, let populate encode per the deployed ABI.
36+
const ref = felts(contract().populate('user_lock', [
37+
{ hashlock: 0x1234abcdn, amount: 1500000000000000000n, reward_amount: 0n,
38+
timelock_delta: 3600, reward_timelock_delta: 0, quote_expiry: 1899999999,
39+
recipient: SOLVER, token: TOKEN, reward_token: '', reward_recipient: '',
40+
src_chain: 'starknet-sepolia', refund_to: USER, payout_curve: ZERO_ADDRESS, payout_curve_data: '' },
41+
{ dst_chain: 'ethereum-sepolia', dst_address: '0x0000000000000000000000000000000000000001',
42+
dst_amount: 1000000n, dst_token: '0x0000000000000000000000000000000000000000' },
43+
String(1721600000000), '',
44+
]).calldata)
45+
// UserLockParams + dst_chain occupy the first 28 felts; the dst_address/dst_token
46+
// ByteArrays use the app's byteArrayFromString convention (unchanged) so compare the
47+
// struct this change owns.
48+
expect(built.slice(0, 28)).toEqual(ref.slice(0, 28))
49+
})
50+
51+
it('defaults payout_curve to ZERO_ADDRESS and refund_to to the user when no curve is set', () => {
52+
const built = buildUserLockTx(baseUserLockParams)
53+
expect(built.entrypoint).toBe('user_lock')
54+
// felt 21 is payout_curve (see layout above) → 0 when no curve
55+
expect(felts(built.calldata)[21]).toBe(0n)
56+
})
57+
58+
it('passes a provided payout curve through', () => {
59+
const curve = '0x27e92c85cf5da7861549ceba60096737a773f9b94f49046604ff0a0035cc351'
60+
const built = felts(buildUserLockTx({ ...baseUserLockParams, payoutCurve: curve }).calldata)
61+
expect(built[21]).toBe(BigInt(curve))
62+
})
63+
})
64+
65+
describe('Starknet buildRefundTx', () => {
66+
it('matches refund_user(hashlock) ABI encoding', () => {
67+
const built = felts(buildRefundTx({ contractAddress: CONTRACT, id: '0x1234abcd' } as any).calldata)
68+
const ref = felts(contract().populate('refund_user', [0x1234abcdn]).calldata)
69+
expect(built).toEqual(ref)
70+
})
71+
})
72+
73+
describe('Starknet buildRedeemSolverTx', () => {
74+
it('matches redeem_solver(hashlock, index, secret) ABI encoding', () => {
75+
const built = felts(buildRedeemSolverTx({ contractAddress: CONTRACT, id: '0x1234abcd', index: 2, secret: '0xdeadbeef' } as any).calldata)
76+
const ref = felts(contract().populate('redeem_solver', [0x1234abcdn, 2n, 0xdeadbeefn]).calldata)
77+
expect(built).toEqual(ref)
78+
})
79+
80+
it('defaults index to 1 when omitted', () => {
81+
const built = felts(buildRedeemSolverTx({ contractAddress: CONTRACT, id: '0x1234abcd', secret: '0xdeadbeef' } as any).calldata)
82+
const ref = felts(contract().populate('redeem_solver', [0x1234abcdn, 1n, 0xdeadbeefn]).calldata)
83+
expect(built).toEqual(ref)
84+
})
85+
})

0 commit comments

Comments
 (0)