@yellow-org/sdk 1.0.1-alpha.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.
- package/README.md +999 -0
- package/dist/abis/generated.d.ts +4300 -0
- package/dist/abis/generated.js +1 -0
- package/dist/app/index.d.ts +2 -0
- package/dist/app/index.js +2 -0
- package/dist/app/packing.d.ts +7 -0
- package/dist/app/packing.js +111 -0
- package/dist/app/types.d.ts +91 -0
- package/dist/app/types.js +42 -0
- package/dist/asset_store.d.ts +13 -0
- package/dist/asset_store.js +121 -0
- package/dist/blockchain/evm/channel_hub_abi.d.ts +4284 -0
- package/dist/blockchain/evm/channel_hub_abi.js +5475 -0
- package/dist/blockchain/evm/client.d.ts +46 -0
- package/dist/blockchain/evm/client.js +428 -0
- package/dist/blockchain/evm/erc20.d.ts +13 -0
- package/dist/blockchain/evm/erc20.js +54 -0
- package/dist/blockchain/evm/erc20_abi.d.ts +144 -0
- package/dist/blockchain/evm/erc20_abi.js +96 -0
- package/dist/blockchain/evm/index.d.ts +6 -0
- package/dist/blockchain/evm/index.js +6 -0
- package/dist/blockchain/evm/interface.d.ts +10 -0
- package/dist/blockchain/evm/interface.js +1 -0
- package/dist/blockchain/evm/types.d.ts +27 -0
- package/dist/blockchain/evm/types.js +1 -0
- package/dist/blockchain/evm/utils.d.ts +9 -0
- package/dist/blockchain/evm/utils.js +129 -0
- package/dist/blockchain/index.d.ts +1 -0
- package/dist/blockchain/index.js +1 -0
- package/dist/client.d.ts +99 -0
- package/dist/client.js +720 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.js +28 -0
- package/dist/core/event.d.ts +29 -0
- package/dist/core/event.js +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +6 -0
- package/dist/core/interface.d.ts +53 -0
- package/dist/core/interface.js +1 -0
- package/dist/core/state.d.ts +21 -0
- package/dist/core/state.js +267 -0
- package/dist/core/state_packer.d.ts +13 -0
- package/dist/core/state_packer.js +98 -0
- package/dist/core/types.d.ts +203 -0
- package/dist/core/types.js +220 -0
- package/dist/core/utils.d.ts +16 -0
- package/dist/core/utils.js +181 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/rpc/api.d.ts +181 -0
- package/dist/rpc/api.js +1 -0
- package/dist/rpc/client.d.ts +36 -0
- package/dist/rpc/client.js +102 -0
- package/dist/rpc/dialer.d.ts +30 -0
- package/dist/rpc/dialer.js +146 -0
- package/dist/rpc/error.d.ts +18 -0
- package/dist/rpc/error.js +27 -0
- package/dist/rpc/index.d.ts +7 -0
- package/dist/rpc/index.js +7 -0
- package/dist/rpc/message.d.ts +25 -0
- package/dist/rpc/message.js +102 -0
- package/dist/rpc/methods.d.ts +30 -0
- package/dist/rpc/methods.js +27 -0
- package/dist/rpc/types.d.ts +101 -0
- package/dist/rpc/types.js +1 -0
- package/dist/signers.d.ts +48 -0
- package/dist/signers.js +96 -0
- package/dist/utils/sign.d.ts +2 -0
- package/dist/utils/sign.js +8 -0
- package/dist/utils.d.ts +42 -0
- package/dist/utils.js +226 -0
- package/package.json +89 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Address } from 'viem';
|
|
2
|
+
import Decimal from 'decimal.js';
|
|
3
|
+
import * as core from '../../core/types';
|
|
4
|
+
import { AssetStore, EVMClient, WalletSigner } from './interface';
|
|
5
|
+
export interface ClientOptions {
|
|
6
|
+
requireCheckAllowance?: boolean;
|
|
7
|
+
requireCheckBalance?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class Client {
|
|
10
|
+
private contractAddress;
|
|
11
|
+
private evmClient;
|
|
12
|
+
private walletSigner;
|
|
13
|
+
private blockchainId;
|
|
14
|
+
private nodeAddress;
|
|
15
|
+
private assetStore;
|
|
16
|
+
private requireCheckAllowance;
|
|
17
|
+
private requireCheckBalance;
|
|
18
|
+
constructor(contractAddress: Address, evmClient: EVMClient, walletSigner: WalletSigner, blockchainId: bigint, nodeAddress: Address, assetStore: AssetStore, options?: ClientOptions);
|
|
19
|
+
private hexToBytes32;
|
|
20
|
+
getAccountsBalances(accounts: Address[], tokens: Address[]): Promise<Decimal[][]>;
|
|
21
|
+
private getAllowance;
|
|
22
|
+
getTokenBalance(asset: string, walletAddress: Address): Promise<Decimal>;
|
|
23
|
+
checkAllowance(asset: string, owner: Address): Promise<Decimal>;
|
|
24
|
+
approve(asset: string, amount: Decimal): Promise<string>;
|
|
25
|
+
approveTokenByAddress(tokenAddress: Address, amount: bigint): Promise<string>;
|
|
26
|
+
checkAllowanceByAddress(tokenAddress: Address, owner: Address): Promise<bigint>;
|
|
27
|
+
getNodeBalance(token: Address): Promise<Decimal>;
|
|
28
|
+
getOpenChannels(user: Address): Promise<string[]>;
|
|
29
|
+
getHomeChannelData(homeChannelId: string): Promise<core.HomeChannelDataResponse>;
|
|
30
|
+
getEscrowDepositData(_escrowChannelId: string): Promise<core.EscrowDepositDataResponse>;
|
|
31
|
+
getEscrowWithdrawalData(_escrowChannelId: string): Promise<core.EscrowWithdrawalDataResponse>;
|
|
32
|
+
deposit(node: Address, token: Address, amount: Decimal): Promise<string>;
|
|
33
|
+
withdraw(node: Address, token: Address, amount: Decimal): Promise<string>;
|
|
34
|
+
create(def: core.ChannelDefinition, initState: core.State): Promise<string>;
|
|
35
|
+
checkpoint(candidate: core.State): Promise<string>;
|
|
36
|
+
challenge(candidate: core.State, challengerSig: `0x${string}`, challengerIdx?: number): Promise<string>;
|
|
37
|
+
close(candidate: core.State): Promise<string>;
|
|
38
|
+
initiateEscrowDeposit(_def: core.ChannelDefinition, _initState: core.State): Promise<string>;
|
|
39
|
+
challengeEscrowDeposit(_candidate: core.State, _challengerSig: `0x${string}`, _challengerIdx?: number): Promise<string>;
|
|
40
|
+
finalizeEscrowDeposit(_candidate: core.State): Promise<string>;
|
|
41
|
+
initiateEscrowWithdrawal(_def: core.ChannelDefinition, _initState: core.State): Promise<string>;
|
|
42
|
+
challengeEscrowWithdrawal(_candidate: core.State, _challengerSig: `0x${string}`, _challengerIdx?: number): Promise<string>;
|
|
43
|
+
finalizeEscrowWithdrawal(_candidate: core.State): Promise<string>;
|
|
44
|
+
migrateChannelHere(_def: core.ChannelDefinition, _candidate: core.State): Promise<string>;
|
|
45
|
+
}
|
|
46
|
+
export declare function newClient(contractAddress: Address, evmClient: EVMClient, walletSigner: WalletSigner, blockchainId: bigint, nodeAddress: Address, assetStore: AssetStore, options?: ClientOptions): Client;
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { hexToBytes, zeroAddress } from 'viem';
|
|
2
|
+
import Decimal from 'decimal.js';
|
|
3
|
+
import * as core from '../../core/types';
|
|
4
|
+
import { decimalToBigInt } from '../../core/utils';
|
|
5
|
+
import { ChannelHubAbi } from './channel_hub_abi';
|
|
6
|
+
import { coreDefToContractDef, coreStateToContractState, contractStateToCoreState, } from './utils';
|
|
7
|
+
import { newERC20 } from './erc20';
|
|
8
|
+
export class Client {
|
|
9
|
+
constructor(contractAddress, evmClient, walletSigner, blockchainId, nodeAddress, assetStore, options) {
|
|
10
|
+
this.contractAddress = contractAddress;
|
|
11
|
+
this.evmClient = evmClient;
|
|
12
|
+
this.walletSigner = walletSigner;
|
|
13
|
+
this.blockchainId = blockchainId;
|
|
14
|
+
this.nodeAddress = nodeAddress;
|
|
15
|
+
this.assetStore = assetStore;
|
|
16
|
+
this.requireCheckAllowance = options?.requireCheckAllowance ?? true;
|
|
17
|
+
this.requireCheckBalance = options?.requireCheckBalance ?? true;
|
|
18
|
+
}
|
|
19
|
+
hexToBytes32(s) {
|
|
20
|
+
const bytes = hexToBytes(s);
|
|
21
|
+
if (bytes.length !== 32) {
|
|
22
|
+
throw new Error(`invalid length: expected 32 bytes, got ${bytes.length}`);
|
|
23
|
+
}
|
|
24
|
+
return `0x${Array.from(bytes)
|
|
25
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
26
|
+
.join('')}`;
|
|
27
|
+
}
|
|
28
|
+
async getAccountsBalances(accounts, tokens) {
|
|
29
|
+
if (accounts.length === 0 || tokens.length === 0) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const result = [];
|
|
33
|
+
for (const account of accounts) {
|
|
34
|
+
const accountBalances = [];
|
|
35
|
+
for (const token of tokens) {
|
|
36
|
+
const balance = (await this.evmClient.readContract({
|
|
37
|
+
address: this.contractAddress,
|
|
38
|
+
abi: ChannelHubAbi,
|
|
39
|
+
functionName: 'getAccountBalance',
|
|
40
|
+
args: [account, token],
|
|
41
|
+
}));
|
|
42
|
+
accountBalances.push(new Decimal(balance.toString()));
|
|
43
|
+
}
|
|
44
|
+
result.push(accountBalances);
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
async getAllowance(asset, owner) {
|
|
49
|
+
const tokenAddress = await this.assetStore.getTokenAddress(asset, this.blockchainId);
|
|
50
|
+
if (tokenAddress === zeroAddress) {
|
|
51
|
+
return new Decimal('1e18');
|
|
52
|
+
}
|
|
53
|
+
const erc20 = newERC20(tokenAddress, this.evmClient);
|
|
54
|
+
const allowance = await erc20.allowance(owner, this.contractAddress);
|
|
55
|
+
const decimals = await this.assetStore.getTokenDecimals(this.blockchainId, tokenAddress);
|
|
56
|
+
return new Decimal(allowance.toString()).div(Decimal.pow(10, decimals));
|
|
57
|
+
}
|
|
58
|
+
async getTokenBalance(asset, walletAddress) {
|
|
59
|
+
const tokenAddress = await this.assetStore.getTokenAddress(asset, this.blockchainId);
|
|
60
|
+
if (tokenAddress === zeroAddress) {
|
|
61
|
+
const balance = await this.evmClient.getBalance({ address: walletAddress });
|
|
62
|
+
return new Decimal(balance.toString()).div(Decimal.pow(10, 18));
|
|
63
|
+
}
|
|
64
|
+
const erc20 = newERC20(tokenAddress, this.evmClient);
|
|
65
|
+
const balance = await erc20.balanceOf(walletAddress);
|
|
66
|
+
const decimals = await this.assetStore.getTokenDecimals(this.blockchainId, tokenAddress);
|
|
67
|
+
return new Decimal(balance.toString()).div(Decimal.pow(10, decimals));
|
|
68
|
+
}
|
|
69
|
+
async checkAllowance(asset, owner) {
|
|
70
|
+
return await this.getAllowance(asset, owner);
|
|
71
|
+
}
|
|
72
|
+
async approve(asset, amount) {
|
|
73
|
+
const tokenAddress = await this.assetStore.getTokenAddress(asset, this.blockchainId);
|
|
74
|
+
if (tokenAddress === zeroAddress) {
|
|
75
|
+
throw new Error('Native tokens do not require approval');
|
|
76
|
+
}
|
|
77
|
+
const decimals = await this.assetStore.getTokenDecimals(this.blockchainId, tokenAddress);
|
|
78
|
+
const amountBig = decimalToBigInt(amount, decimals);
|
|
79
|
+
const erc20 = newERC20(tokenAddress, this.evmClient, this.walletSigner);
|
|
80
|
+
return await erc20.approve(this.contractAddress, amountBig);
|
|
81
|
+
}
|
|
82
|
+
async approveTokenByAddress(tokenAddress, amount) {
|
|
83
|
+
const erc20 = newERC20(tokenAddress, this.evmClient, this.walletSigner);
|
|
84
|
+
return await erc20.approve(this.contractAddress, amount);
|
|
85
|
+
}
|
|
86
|
+
async checkAllowanceByAddress(tokenAddress, owner) {
|
|
87
|
+
const erc20 = newERC20(tokenAddress, this.evmClient);
|
|
88
|
+
return await erc20.allowance(owner, this.contractAddress);
|
|
89
|
+
}
|
|
90
|
+
async getNodeBalance(token) {
|
|
91
|
+
const balance = (await this.evmClient.readContract({
|
|
92
|
+
address: this.contractAddress,
|
|
93
|
+
abi: ChannelHubAbi,
|
|
94
|
+
functionName: 'getAccountBalance',
|
|
95
|
+
args: [this.nodeAddress, token],
|
|
96
|
+
}));
|
|
97
|
+
const decimals = await this.assetStore.getTokenDecimals(this.blockchainId, token);
|
|
98
|
+
return new Decimal(balance.toString()).div(Decimal.pow(10, decimals));
|
|
99
|
+
}
|
|
100
|
+
async getOpenChannels(user) {
|
|
101
|
+
const channelIds = (await this.evmClient.readContract({
|
|
102
|
+
address: this.contractAddress,
|
|
103
|
+
abi: ChannelHubAbi,
|
|
104
|
+
functionName: 'getOpenChannels',
|
|
105
|
+
args: [user],
|
|
106
|
+
}));
|
|
107
|
+
return channelIds.map((id) => id);
|
|
108
|
+
}
|
|
109
|
+
async getHomeChannelData(homeChannelId) {
|
|
110
|
+
const channelIdBytes = this.hexToBytes32(homeChannelId);
|
|
111
|
+
const data = (await this.evmClient.readContract({
|
|
112
|
+
address: this.contractAddress,
|
|
113
|
+
abi: ChannelHubAbi,
|
|
114
|
+
functionName: 'getChannelData',
|
|
115
|
+
args: [channelIdBytes],
|
|
116
|
+
}));
|
|
117
|
+
const [, definition, lastState, challengeExpiry] = Array.isArray(data) ? data : [data.status, data.definition, data.lastState, data.challengeExpiry, data.lockedFunds];
|
|
118
|
+
const coreState = contractStateToCoreState(lastState, homeChannelId);
|
|
119
|
+
return {
|
|
120
|
+
definition: {
|
|
121
|
+
nonce: definition.nonce,
|
|
122
|
+
challenge: definition.challengeDuration,
|
|
123
|
+
approvedSigValidators: '0x' + (definition.approvedSignatureValidators ?? 0n).toString(16),
|
|
124
|
+
},
|
|
125
|
+
node: definition.node,
|
|
126
|
+
lastState: coreState,
|
|
127
|
+
challengeExpiry,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
async getEscrowDepositData(_escrowChannelId) {
|
|
131
|
+
throw new Error('getEscrowDepositData not implemented - needs contract ABI update');
|
|
132
|
+
}
|
|
133
|
+
async getEscrowWithdrawalData(_escrowChannelId) {
|
|
134
|
+
throw new Error('getEscrowWithdrawalData not implemented - needs contract ABI update');
|
|
135
|
+
}
|
|
136
|
+
async deposit(node, token, amount) {
|
|
137
|
+
const decimals = await this.assetStore.getTokenDecimals(this.blockchainId, token);
|
|
138
|
+
const amountBig = decimalToBigInt(amount, decimals);
|
|
139
|
+
console.log('💳 EVM Client - Deposit transaction:', {
|
|
140
|
+
contractAddress: this.contractAddress,
|
|
141
|
+
blockchainId: this.blockchainId.toString(),
|
|
142
|
+
node,
|
|
143
|
+
token,
|
|
144
|
+
amount: amount.toString(),
|
|
145
|
+
amountBig: amountBig.toString(),
|
|
146
|
+
walletChain: this.walletSigner.chain?.id,
|
|
147
|
+
walletChainName: this.walletSigner.chain?.name
|
|
148
|
+
});
|
|
149
|
+
try {
|
|
150
|
+
console.log('🔍 Simulating deposit...');
|
|
151
|
+
const { request } = await this.evmClient.simulateContract({
|
|
152
|
+
address: this.contractAddress,
|
|
153
|
+
abi: ChannelHubAbi,
|
|
154
|
+
functionName: 'depositToVault',
|
|
155
|
+
args: [node, token, amountBig],
|
|
156
|
+
account: this.walletSigner.account.address,
|
|
157
|
+
...(token === zeroAddress ? { value: amountBig } : {}),
|
|
158
|
+
});
|
|
159
|
+
console.log('✅ Simulation successful - executing deposit...');
|
|
160
|
+
const hash = await this.walletSigner.writeContract(request);
|
|
161
|
+
console.log('📤 Deposit transaction submitted - hash:', hash);
|
|
162
|
+
console.log('⏳ Waiting for confirmation...');
|
|
163
|
+
const receipt = await this.evmClient.waitForTransactionReceipt({ hash });
|
|
164
|
+
console.log('✅ Deposit transaction confirmed!', {
|
|
165
|
+
blockNumber: receipt.blockNumber,
|
|
166
|
+
gasUsed: receipt.gasUsed.toString(),
|
|
167
|
+
});
|
|
168
|
+
return hash;
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error('❌ Deposit transaction failed at blockchain level');
|
|
172
|
+
if (error.message?.includes('not supported') || error.message?.includes('not available')) {
|
|
173
|
+
console.error('⚠️ RPC ENDPOINT ISSUE: The RPC endpoint does not support sending transactions.');
|
|
174
|
+
console.error(' This usually means the RPC only supports read operations (eth_call, eth_getBalance, etc.)');
|
|
175
|
+
console.error(' but not write operations (eth_sendTransaction).');
|
|
176
|
+
console.error(' Solutions:');
|
|
177
|
+
console.error(' 1. Use an RPC provider that supports transactions (Infura, Alchemy, etc.)');
|
|
178
|
+
console.error(' 2. Make sure your RPC endpoint includes transaction capabilities');
|
|
179
|
+
}
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async withdraw(node, token, amount) {
|
|
184
|
+
const decimals = await this.assetStore.getTokenDecimals(this.blockchainId, token);
|
|
185
|
+
const amountBig = decimalToBigInt(amount, decimals);
|
|
186
|
+
console.log('💳 EVM Client - Withdraw transaction:', {
|
|
187
|
+
contractAddress: this.contractAddress,
|
|
188
|
+
blockchainId: this.blockchainId.toString(),
|
|
189
|
+
node,
|
|
190
|
+
token,
|
|
191
|
+
amount: amount.toString(),
|
|
192
|
+
amountBig: amountBig.toString(),
|
|
193
|
+
walletChain: this.walletSigner.chain?.id,
|
|
194
|
+
walletChainName: this.walletSigner.chain?.name
|
|
195
|
+
});
|
|
196
|
+
try {
|
|
197
|
+
console.log('🔍 Simulating withdrawal...');
|
|
198
|
+
const { request } = await this.evmClient.simulateContract({
|
|
199
|
+
address: this.contractAddress,
|
|
200
|
+
abi: ChannelHubAbi,
|
|
201
|
+
functionName: 'withdrawFromVault',
|
|
202
|
+
args: [node, token, amountBig],
|
|
203
|
+
account: this.walletSigner.account.address,
|
|
204
|
+
});
|
|
205
|
+
console.log('✅ Simulation successful - executing withdrawal...');
|
|
206
|
+
const hash = await this.walletSigner.writeContract(request);
|
|
207
|
+
console.log('📤 Withdraw transaction submitted - hash:', hash);
|
|
208
|
+
console.log('⏳ Waiting for confirmation...');
|
|
209
|
+
const receipt = await this.evmClient.waitForTransactionReceipt({ hash });
|
|
210
|
+
console.log('✅ Withdraw transaction confirmed!', {
|
|
211
|
+
blockNumber: receipt.blockNumber,
|
|
212
|
+
gasUsed: receipt.gasUsed.toString(),
|
|
213
|
+
});
|
|
214
|
+
return hash;
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
console.error('❌ Withdraw simulation/execution failed!');
|
|
218
|
+
if (error.message) {
|
|
219
|
+
console.error(' Reason:', error.message);
|
|
220
|
+
}
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
async create(def, initState) {
|
|
225
|
+
const contractDef = coreDefToContractDef(def, initState.asset, initState.userWallet, this.nodeAddress);
|
|
226
|
+
const contractState = await coreStateToContractState(initState, (blockchainId, tokenAddress) => this.assetStore.getTokenDecimals(blockchainId, tokenAddress));
|
|
227
|
+
if (initState.transition.type === core.TransitionType.HomeDeposit ||
|
|
228
|
+
initState.transition.type === core.TransitionType.EscrowDeposit) {
|
|
229
|
+
if (this.requireCheckAllowance) {
|
|
230
|
+
const allowance = await this.getAllowance(initState.asset, initState.userWallet);
|
|
231
|
+
if (allowance.lessThan(initState.transition.amount)) {
|
|
232
|
+
throw new Error('Allowance is not sufficient to cover the deposit amount');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (this.requireCheckBalance) {
|
|
236
|
+
const balance = await this.getTokenBalance(initState.asset, initState.userWallet);
|
|
237
|
+
if (balance.lessThan(initState.transition.amount)) {
|
|
238
|
+
throw new Error('Balance is not sufficient to cover the deposit amount');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
console.log('💳 EVM Client - Create channel transaction:', {
|
|
243
|
+
contractAddress: this.contractAddress,
|
|
244
|
+
blockchainId: this.blockchainId.toString(),
|
|
245
|
+
walletChain: this.walletSigner.chain?.id,
|
|
246
|
+
walletChainName: this.walletSigner.chain?.name
|
|
247
|
+
});
|
|
248
|
+
let nativeValue;
|
|
249
|
+
if ((initState.transition.type === core.TransitionType.HomeDeposit ||
|
|
250
|
+
initState.transition.type === core.TransitionType.EscrowDeposit) &&
|
|
251
|
+
contractState.homeLedger.token === zeroAddress) {
|
|
252
|
+
nativeValue = decimalToBigInt(initState.transition.amount, contractState.homeLedger.decimals);
|
|
253
|
+
}
|
|
254
|
+
console.log('🔍 Simulating transaction...');
|
|
255
|
+
try {
|
|
256
|
+
const { request } = (await this.evmClient.simulateContract({
|
|
257
|
+
address: this.contractAddress,
|
|
258
|
+
abi: ChannelHubAbi,
|
|
259
|
+
functionName: 'createChannel',
|
|
260
|
+
args: [contractDef, contractState],
|
|
261
|
+
account: this.walletSigner.account.address,
|
|
262
|
+
...(nativeValue != null ? { value: nativeValue } : {}),
|
|
263
|
+
}));
|
|
264
|
+
console.log('✅ Simulation successful - executing transaction...');
|
|
265
|
+
const hash = await this.walletSigner.writeContract(request);
|
|
266
|
+
console.log('📤 Transaction submitted - hash:', hash);
|
|
267
|
+
console.log('⏳ Waiting for confirmation...');
|
|
268
|
+
const receipt = await this.evmClient.waitForTransactionReceipt({ hash });
|
|
269
|
+
console.log('✅ Create channel transaction confirmed!', {
|
|
270
|
+
blockNumber: receipt.blockNumber,
|
|
271
|
+
gasUsed: receipt.gasUsed.toString(),
|
|
272
|
+
});
|
|
273
|
+
return hash;
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
console.error('❌ Transaction simulation failed!');
|
|
277
|
+
console.error(' This means the transaction would revert on-chain.');
|
|
278
|
+
if (error.message) {
|
|
279
|
+
console.error(' Revert reason:', error.message);
|
|
280
|
+
}
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
async checkpoint(candidate) {
|
|
285
|
+
if (!candidate.homeChannelId) {
|
|
286
|
+
throw new Error('Candidate state must have a home channel ID');
|
|
287
|
+
}
|
|
288
|
+
const channelIdBytes = this.hexToBytes32(candidate.homeChannelId);
|
|
289
|
+
const contractCandidate = await coreStateToContractState(candidate, (blockchainId, tokenAddress) => this.assetStore.getTokenDecimals(blockchainId, tokenAddress));
|
|
290
|
+
if (candidate.transition.type === core.TransitionType.HomeDeposit) {
|
|
291
|
+
if (this.requireCheckAllowance) {
|
|
292
|
+
const allowance = await this.getAllowance(candidate.asset, candidate.userWallet);
|
|
293
|
+
if (allowance.lessThan(candidate.transition.amount)) {
|
|
294
|
+
throw new Error('Allowance is not sufficient to cover the deposit amount');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (this.requireCheckBalance) {
|
|
298
|
+
const balance = await this.getTokenBalance(candidate.asset, candidate.userWallet);
|
|
299
|
+
if (balance.lessThan(candidate.transition.amount)) {
|
|
300
|
+
throw new Error('Balance is not sufficient to cover the deposit amount');
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const nativeValue = contractCandidate.homeLedger.token === zeroAddress
|
|
304
|
+
? decimalToBigInt(candidate.transition.amount, contractCandidate.homeLedger.decimals)
|
|
305
|
+
: undefined;
|
|
306
|
+
console.log('💳 EVM Client - Deposit to channel transaction:', {
|
|
307
|
+
contractAddress: this.contractAddress,
|
|
308
|
+
blockchainId: this.blockchainId.toString(),
|
|
309
|
+
channelId: channelIdBytes,
|
|
310
|
+
walletChain: this.walletSigner.chain?.id
|
|
311
|
+
});
|
|
312
|
+
const hash = await this.walletSigner.writeContract({
|
|
313
|
+
address: this.contractAddress,
|
|
314
|
+
abi: ChannelHubAbi,
|
|
315
|
+
functionName: 'depositToChannel',
|
|
316
|
+
args: [channelIdBytes, contractCandidate],
|
|
317
|
+
gas: 5000000n,
|
|
318
|
+
...(nativeValue != null ? { value: nativeValue } : {}),
|
|
319
|
+
});
|
|
320
|
+
console.log('✅ Deposit to channel transaction hash:', hash);
|
|
321
|
+
return hash;
|
|
322
|
+
}
|
|
323
|
+
if (candidate.transition.type === core.TransitionType.HomeWithdrawal) {
|
|
324
|
+
console.log('💳 EVM Client - Withdraw from channel transaction:', {
|
|
325
|
+
contractAddress: this.contractAddress,
|
|
326
|
+
blockchainId: this.blockchainId.toString(),
|
|
327
|
+
channelId: channelIdBytes,
|
|
328
|
+
walletChain: this.walletSigner.chain?.id
|
|
329
|
+
});
|
|
330
|
+
const hash = await this.walletSigner.writeContract({
|
|
331
|
+
address: this.contractAddress,
|
|
332
|
+
abi: ChannelHubAbi,
|
|
333
|
+
functionName: 'withdrawFromChannel',
|
|
334
|
+
args: [channelIdBytes, contractCandidate],
|
|
335
|
+
gas: 5000000n,
|
|
336
|
+
});
|
|
337
|
+
console.log('✅ Withdraw from channel transaction hash:', hash);
|
|
338
|
+
return hash;
|
|
339
|
+
}
|
|
340
|
+
console.log('💳 EVM Client - Checkpoint channel transaction:', {
|
|
341
|
+
contractAddress: this.contractAddress,
|
|
342
|
+
blockchainId: this.blockchainId.toString(),
|
|
343
|
+
channelId: channelIdBytes,
|
|
344
|
+
walletChain: this.walletSigner.chain?.id
|
|
345
|
+
});
|
|
346
|
+
const hash = await this.walletSigner.writeContract({
|
|
347
|
+
address: this.contractAddress,
|
|
348
|
+
abi: ChannelHubAbi,
|
|
349
|
+
functionName: 'checkpointChannel',
|
|
350
|
+
args: [channelIdBytes, contractCandidate],
|
|
351
|
+
gas: 5000000n,
|
|
352
|
+
});
|
|
353
|
+
console.log('✅ Checkpoint channel transaction hash:', hash);
|
|
354
|
+
return hash;
|
|
355
|
+
}
|
|
356
|
+
async challenge(candidate, challengerSig, challengerIdx = 0) {
|
|
357
|
+
if (!candidate.homeChannelId) {
|
|
358
|
+
throw new Error('Candidate state must have a home channel ID');
|
|
359
|
+
}
|
|
360
|
+
const channelIdBytes = this.hexToBytes32(candidate.homeChannelId);
|
|
361
|
+
const contractCandidate = await coreStateToContractState(candidate, (blockchainId, tokenAddress) => this.assetStore.getTokenDecimals(blockchainId, tokenAddress));
|
|
362
|
+
console.log('💳 EVM Client - Challenge channel transaction:', {
|
|
363
|
+
contractAddress: this.contractAddress,
|
|
364
|
+
blockchainId: this.blockchainId.toString(),
|
|
365
|
+
channelId: channelIdBytes,
|
|
366
|
+
walletChain: this.walletSigner.chain?.id
|
|
367
|
+
});
|
|
368
|
+
const hash = await this.walletSigner.writeContract({
|
|
369
|
+
address: this.contractAddress,
|
|
370
|
+
abi: ChannelHubAbi,
|
|
371
|
+
functionName: 'challengeChannel',
|
|
372
|
+
args: [channelIdBytes, contractCandidate, challengerSig, challengerIdx],
|
|
373
|
+
gas: 5000000n,
|
|
374
|
+
});
|
|
375
|
+
console.log('✅ Challenge channel transaction hash:', hash);
|
|
376
|
+
return hash;
|
|
377
|
+
}
|
|
378
|
+
async close(candidate) {
|
|
379
|
+
if (!candidate.homeChannelId) {
|
|
380
|
+
throw new Error('Candidate state must have a home channel ID');
|
|
381
|
+
}
|
|
382
|
+
const channelIdBytes = this.hexToBytes32(candidate.homeChannelId);
|
|
383
|
+
const contractCandidate = await coreStateToContractState(candidate, (blockchainId, tokenAddress) => this.assetStore.getTokenDecimals(blockchainId, tokenAddress));
|
|
384
|
+
if (candidate.transition.type !== core.TransitionType.Finalize) {
|
|
385
|
+
throw new Error('Unsupported intent for close');
|
|
386
|
+
}
|
|
387
|
+
console.log('💳 EVM Client - Close channel transaction:', {
|
|
388
|
+
contractAddress: this.contractAddress,
|
|
389
|
+
blockchainId: this.blockchainId.toString(),
|
|
390
|
+
channelId: channelIdBytes,
|
|
391
|
+
walletChain: this.walletSigner.chain?.id,
|
|
392
|
+
walletChainName: this.walletSigner.chain?.name
|
|
393
|
+
});
|
|
394
|
+
const hash = await this.walletSigner.writeContract({
|
|
395
|
+
address: this.contractAddress,
|
|
396
|
+
abi: ChannelHubAbi,
|
|
397
|
+
functionName: 'closeChannel',
|
|
398
|
+
args: [channelIdBytes, contractCandidate],
|
|
399
|
+
gas: 5000000n,
|
|
400
|
+
});
|
|
401
|
+
console.log('✅ Close channel transaction hash:', hash);
|
|
402
|
+
return hash;
|
|
403
|
+
}
|
|
404
|
+
async initiateEscrowDeposit(_def, _initState) {
|
|
405
|
+
throw new Error('initiateEscrowDeposit not implemented - needs contract ABI update');
|
|
406
|
+
}
|
|
407
|
+
async challengeEscrowDeposit(_candidate, _challengerSig, _challengerIdx = 0) {
|
|
408
|
+
throw new Error('challengeEscrowDeposit not implemented - needs contract ABI update');
|
|
409
|
+
}
|
|
410
|
+
async finalizeEscrowDeposit(_candidate) {
|
|
411
|
+
throw new Error('finalizeEscrowDeposit not implemented - needs contract ABI update');
|
|
412
|
+
}
|
|
413
|
+
async initiateEscrowWithdrawal(_def, _initState) {
|
|
414
|
+
throw new Error('initiateEscrowWithdrawal not implemented - needs contract ABI update');
|
|
415
|
+
}
|
|
416
|
+
async challengeEscrowWithdrawal(_candidate, _challengerSig, _challengerIdx = 0) {
|
|
417
|
+
throw new Error('challengeEscrowWithdrawal not implemented - needs contract ABI update');
|
|
418
|
+
}
|
|
419
|
+
async finalizeEscrowWithdrawal(_candidate) {
|
|
420
|
+
throw new Error('finalizeEscrowWithdrawal not implemented - needs contract ABI update');
|
|
421
|
+
}
|
|
422
|
+
async migrateChannelHere(_def, _candidate) {
|
|
423
|
+
throw new Error('migrateChannelHere not implemented - needs contract ABI update');
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
export function newClient(contractAddress, evmClient, walletSigner, blockchainId, nodeAddress, assetStore, options) {
|
|
427
|
+
return new Client(contractAddress, evmClient, walletSigner, blockchainId, nodeAddress, assetStore, options);
|
|
428
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Address } from 'viem';
|
|
2
|
+
import { EVMClient, WalletSigner } from './interface';
|
|
3
|
+
export declare class ERC20 {
|
|
4
|
+
private tokenAddress;
|
|
5
|
+
private client;
|
|
6
|
+
private walletSigner?;
|
|
7
|
+
constructor(tokenAddress: Address, client: EVMClient, walletSigner?: WalletSigner);
|
|
8
|
+
balanceOf(account: Address): Promise<bigint>;
|
|
9
|
+
allowance(owner: Address, spender: Address): Promise<bigint>;
|
|
10
|
+
approve(spender: Address, amount: bigint): Promise<string>;
|
|
11
|
+
decimals(): Promise<number>;
|
|
12
|
+
}
|
|
13
|
+
export declare function newERC20(tokenAddress: Address, client: EVMClient, walletSigner?: WalletSigner): ERC20;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Erc20Abi } from './erc20_abi';
|
|
2
|
+
export class ERC20 {
|
|
3
|
+
constructor(tokenAddress, client, walletSigner) {
|
|
4
|
+
this.tokenAddress = tokenAddress;
|
|
5
|
+
this.client = client;
|
|
6
|
+
this.walletSigner = walletSigner;
|
|
7
|
+
}
|
|
8
|
+
async balanceOf(account) {
|
|
9
|
+
return this.client.readContract({
|
|
10
|
+
address: this.tokenAddress,
|
|
11
|
+
abi: Erc20Abi,
|
|
12
|
+
functionName: 'balanceOf',
|
|
13
|
+
args: [account],
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
async allowance(owner, spender) {
|
|
17
|
+
return this.client.readContract({
|
|
18
|
+
address: this.tokenAddress,
|
|
19
|
+
abi: Erc20Abi,
|
|
20
|
+
functionName: 'allowance',
|
|
21
|
+
args: [owner, spender],
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async approve(spender, amount) {
|
|
25
|
+
if (!this.walletSigner) {
|
|
26
|
+
throw new Error('Wallet signer is required for approve operation');
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const { request } = (await this.client.simulateContract({
|
|
30
|
+
address: this.tokenAddress,
|
|
31
|
+
abi: Erc20Abi,
|
|
32
|
+
functionName: 'approve',
|
|
33
|
+
args: [spender, amount],
|
|
34
|
+
account: this.walletSigner.account.address,
|
|
35
|
+
}));
|
|
36
|
+
const hash = await this.walletSigner.writeContract(request);
|
|
37
|
+
await this.client.waitForTransactionReceipt({ hash });
|
|
38
|
+
return hash;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error('❌ Approve simulation/execution failed!');
|
|
42
|
+
if (error.message) {
|
|
43
|
+
console.error(' Reason:', error.message);
|
|
44
|
+
}
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async decimals() {
|
|
49
|
+
throw new Error('decimals() not available in minimal ERC20 ABI');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function newERC20(tokenAddress, client, walletSigner) {
|
|
53
|
+
return new ERC20(tokenAddress, client, walletSigner);
|
|
54
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
export declare const Erc20Abi: readonly [{
|
|
2
|
+
readonly type: "function";
|
|
3
|
+
readonly name: "name";
|
|
4
|
+
readonly inputs: readonly [];
|
|
5
|
+
readonly outputs: readonly [{
|
|
6
|
+
readonly name: "";
|
|
7
|
+
readonly type: "string";
|
|
8
|
+
}];
|
|
9
|
+
readonly stateMutability: "view";
|
|
10
|
+
}, {
|
|
11
|
+
readonly type: "function";
|
|
12
|
+
readonly name: "symbol";
|
|
13
|
+
readonly inputs: readonly [];
|
|
14
|
+
readonly outputs: readonly [{
|
|
15
|
+
readonly name: "";
|
|
16
|
+
readonly type: "string";
|
|
17
|
+
}];
|
|
18
|
+
readonly stateMutability: "view";
|
|
19
|
+
}, {
|
|
20
|
+
readonly type: "function";
|
|
21
|
+
readonly name: "decimals";
|
|
22
|
+
readonly inputs: readonly [];
|
|
23
|
+
readonly outputs: readonly [{
|
|
24
|
+
readonly name: "";
|
|
25
|
+
readonly type: "uint8";
|
|
26
|
+
}];
|
|
27
|
+
readonly stateMutability: "view";
|
|
28
|
+
}, {
|
|
29
|
+
readonly type: "function";
|
|
30
|
+
readonly name: "totalSupply";
|
|
31
|
+
readonly inputs: readonly [];
|
|
32
|
+
readonly outputs: readonly [{
|
|
33
|
+
readonly name: "";
|
|
34
|
+
readonly type: "uint256";
|
|
35
|
+
}];
|
|
36
|
+
readonly stateMutability: "view";
|
|
37
|
+
}, {
|
|
38
|
+
readonly type: "function";
|
|
39
|
+
readonly name: "balanceOf";
|
|
40
|
+
readonly inputs: readonly [{
|
|
41
|
+
readonly name: "account";
|
|
42
|
+
readonly type: "address";
|
|
43
|
+
}];
|
|
44
|
+
readonly outputs: readonly [{
|
|
45
|
+
readonly name: "";
|
|
46
|
+
readonly type: "uint256";
|
|
47
|
+
}];
|
|
48
|
+
readonly stateMutability: "view";
|
|
49
|
+
}, {
|
|
50
|
+
readonly type: "function";
|
|
51
|
+
readonly name: "transfer";
|
|
52
|
+
readonly inputs: readonly [{
|
|
53
|
+
readonly name: "to";
|
|
54
|
+
readonly type: "address";
|
|
55
|
+
}, {
|
|
56
|
+
readonly name: "amount";
|
|
57
|
+
readonly type: "uint256";
|
|
58
|
+
}];
|
|
59
|
+
readonly outputs: readonly [{
|
|
60
|
+
readonly name: "";
|
|
61
|
+
readonly type: "bool";
|
|
62
|
+
}];
|
|
63
|
+
readonly stateMutability: "nonpayable";
|
|
64
|
+
}, {
|
|
65
|
+
readonly type: "function";
|
|
66
|
+
readonly name: "allowance";
|
|
67
|
+
readonly inputs: readonly [{
|
|
68
|
+
readonly name: "owner";
|
|
69
|
+
readonly type: "address";
|
|
70
|
+
}, {
|
|
71
|
+
readonly name: "spender";
|
|
72
|
+
readonly type: "address";
|
|
73
|
+
}];
|
|
74
|
+
readonly outputs: readonly [{
|
|
75
|
+
readonly name: "";
|
|
76
|
+
readonly type: "uint256";
|
|
77
|
+
}];
|
|
78
|
+
readonly stateMutability: "view";
|
|
79
|
+
}, {
|
|
80
|
+
readonly type: "function";
|
|
81
|
+
readonly name: "approve";
|
|
82
|
+
readonly inputs: readonly [{
|
|
83
|
+
readonly name: "spender";
|
|
84
|
+
readonly type: "address";
|
|
85
|
+
}, {
|
|
86
|
+
readonly name: "amount";
|
|
87
|
+
readonly type: "uint256";
|
|
88
|
+
}];
|
|
89
|
+
readonly outputs: readonly [{
|
|
90
|
+
readonly name: "";
|
|
91
|
+
readonly type: "bool";
|
|
92
|
+
}];
|
|
93
|
+
readonly stateMutability: "nonpayable";
|
|
94
|
+
}, {
|
|
95
|
+
readonly type: "function";
|
|
96
|
+
readonly name: "transferFrom";
|
|
97
|
+
readonly inputs: readonly [{
|
|
98
|
+
readonly name: "from";
|
|
99
|
+
readonly type: "address";
|
|
100
|
+
}, {
|
|
101
|
+
readonly name: "to";
|
|
102
|
+
readonly type: "address";
|
|
103
|
+
}, {
|
|
104
|
+
readonly name: "amount";
|
|
105
|
+
readonly type: "uint256";
|
|
106
|
+
}];
|
|
107
|
+
readonly outputs: readonly [{
|
|
108
|
+
readonly name: "";
|
|
109
|
+
readonly type: "bool";
|
|
110
|
+
}];
|
|
111
|
+
readonly stateMutability: "nonpayable";
|
|
112
|
+
}, {
|
|
113
|
+
readonly type: "event";
|
|
114
|
+
readonly name: "Transfer";
|
|
115
|
+
readonly inputs: readonly [{
|
|
116
|
+
readonly name: "from";
|
|
117
|
+
readonly type: "address";
|
|
118
|
+
readonly indexed: true;
|
|
119
|
+
}, {
|
|
120
|
+
readonly name: "to";
|
|
121
|
+
readonly type: "address";
|
|
122
|
+
readonly indexed: true;
|
|
123
|
+
}, {
|
|
124
|
+
readonly name: "value";
|
|
125
|
+
readonly type: "uint256";
|
|
126
|
+
readonly indexed: false;
|
|
127
|
+
}];
|
|
128
|
+
}, {
|
|
129
|
+
readonly type: "event";
|
|
130
|
+
readonly name: "Approval";
|
|
131
|
+
readonly inputs: readonly [{
|
|
132
|
+
readonly name: "owner";
|
|
133
|
+
readonly type: "address";
|
|
134
|
+
readonly indexed: true;
|
|
135
|
+
}, {
|
|
136
|
+
readonly name: "spender";
|
|
137
|
+
readonly type: "address";
|
|
138
|
+
readonly indexed: true;
|
|
139
|
+
}, {
|
|
140
|
+
readonly name: "value";
|
|
141
|
+
readonly type: "uint256";
|
|
142
|
+
readonly indexed: false;
|
|
143
|
+
}];
|
|
144
|
+
}];
|