@snowbridge/provider-ethers 1.0.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.
@@ -0,0 +1,5 @@
1
+
2
+ 
3
+ > @snowbridge/provider-ethers@1.0.0 build /Users/alistairsingh/c/snowbridge/web/packages/provider-ethers
4
+ > tsc --build --force
5
+
@@ -0,0 +1,6 @@
1
+
2
+ 
3
+ > @snowbridge/provider-ethers@1.0.0 format /Users/alistairsingh/c/snowbridge/web/packages/provider-ethers
4
+ > prettier src --write
5
+
6
+ src/index.tssrc/index.ts 64ms (unchanged)
@@ -0,0 +1,48 @@
1
+ import { AbstractProvider, Contract, ContractTransaction, Interface, InterfaceAbi, TransactionReceipt } from "ethers";
2
+ import type { DepositParamsStruct, EthereumProvider, EthereumProviderTypes, FeeData, GatewayV1OutboundMessageAccepted, GatewayV2OutboundMessageAccepted, IGatewayV1, IGatewayV2, L1AdapterDepositParams, L1LegacySwapRouterExactOutputSingleParams, L1SwapRouterExactOutputSingleParams, MultiAddressStruct, SendParamsStruct, SwapParamsStruct } from "@snowbridge/base-types";
3
+ export interface EthersProviderTypes extends EthereumProviderTypes {
4
+ Connection: AbstractProvider;
5
+ Contract: Contract;
6
+ Abi: InterfaceAbi;
7
+ TransactionReceipt: TransactionReceipt;
8
+ ContractTransaction: ContractTransaction;
9
+ }
10
+ export declare class EthersEthereumProvider implements EthereumProvider<EthersProviderTypes> {
11
+ readonly providerTypes: EthersProviderTypes;
12
+ static gatewayV1Interface: Interface;
13
+ static gatewayV2Interface: Interface;
14
+ createProvider(url: string): AbstractProvider;
15
+ destroyProvider(provider: AbstractProvider): void;
16
+ destroyContract(contract: Contract): Promise<void>;
17
+ connectContract(address: string, abi: InterfaceAbi, provider: AbstractProvider): Contract;
18
+ erc20Balance(provider: AbstractProvider, tokenAddress: string, owner: string, spender: string): Promise<{
19
+ balance: bigint;
20
+ gatewayAllowance: bigint;
21
+ }>;
22
+ gatewayV1SendToken(provider: AbstractProvider, gatewayAddress: string, sender: string, token: string, destinationChain: number, destinationAddress: MultiAddressStruct, destinationFee: bigint, amount: bigint, value: bigint): Promise<ContractTransaction>;
23
+ gatewayV2RegisterToken(provider: AbstractProvider, gatewayAddress: string, sender: string, token: string, network: number, executionFee: bigint, relayerFee: bigint, value: bigint): Promise<ContractTransaction>;
24
+ gatewayV2CreateAgent(provider: AbstractProvider, gatewayAddress: string, id: string): Promise<ContractTransaction>;
25
+ gatewayV2SendMessage(provider: AbstractProvider, gatewayAddress: string, sender: string, xcm: Uint8Array, assets: string[], claimer: Uint8Array, executionFee: bigint, relayerFee: bigint, value: bigint): Promise<ContractTransaction>;
26
+ l2AdapterSendEtherAndCall(provider: AbstractProvider, adapterAddress: string, sender: string, params: DepositParamsStruct, sendParams: SendParamsStruct, recipient: string, topic: string, value?: bigint): Promise<ContractTransaction>;
27
+ l2AdapterSendTokenAndCall(provider: AbstractProvider, adapterAddress: string, sender: string, params: DepositParamsStruct, swapParams: SwapParamsStruct, sendParams: SendParamsStruct, recipient: string, topic: string): Promise<ContractTransaction>;
28
+ evmParachainTransferAssetsUsingTypeAndThenAddress(provider: AbstractProvider, precompileAddress: string, sourceAccount: string, destination: [number, string[]], assets: [string, bigint][], assetsTransferType: number, remoteFeesIdIndex: number, feesTransferType: number, customXcmHex: string): Promise<ContractTransaction>;
29
+ encodeFunctionData(abi: InterfaceAbi, method: string, args: readonly unknown[]): string;
30
+ decodeFunctionResult<T = unknown>(abi: InterfaceAbi, method: string, data: string): T;
31
+ encodeNativeAsset(tokenAddress: string, amount: bigint): string;
32
+ l1AdapterDepositNativeEther(params: L1AdapterDepositParams, recipient: string, topic: string): string;
33
+ l1AdapterDepositToken(params: L1AdapterDepositParams, recipient: string, topic: string): string;
34
+ l1SwapRouterExactOutputSingle(params: L1SwapRouterExactOutputSingleParams): string;
35
+ l1LegacySwapRouterExactOutputSingle(params: L1LegacySwapRouterExactOutputSingleParams): string;
36
+ beneficiaryMultiAddress(beneficiary: string): MultiAddressStruct;
37
+ estimateGas(provider: AbstractProvider, tx: ContractTransaction): Promise<bigint>;
38
+ getTransactionCount(provider: AbstractProvider, address: string, blockTag?: "latest" | "pending"): Promise<number>;
39
+ getBalance(provider: AbstractProvider, address: string): Promise<bigint>;
40
+ getFeeData(provider: AbstractProvider): Promise<FeeData>;
41
+ parseUnits(value: string, decimals: number): bigint;
42
+ gatewayOperatingMode(gateway: Contract & (IGatewayV1 | IGatewayV2)): Promise<bigint>;
43
+ gatewayChannelOperatingModeOf(gateway: Contract & IGatewayV1, channelId: string): Promise<bigint>;
44
+ isContractAddress(provider: AbstractProvider, address: string): Promise<boolean>;
45
+ scanGatewayV1OutboundMessageAccepted(receipt: TransactionReceipt): GatewayV1OutboundMessageAccepted | null;
46
+ scanGatewayV2OutboundMessageAccepted(receipt: TransactionReceipt): GatewayV2OutboundMessageAccepted | null;
47
+ }
48
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EthersEthereumProvider = void 0;
4
+ const ethers_1 = require("ethers");
5
+ const base_types_1 = require("@snowbridge/base-types");
6
+ class EthersEthereumProvider {
7
+ createProvider(url) {
8
+ if (url.startsWith("http")) {
9
+ return new ethers_1.JsonRpcProvider(url);
10
+ }
11
+ return new ethers_1.WebSocketProvider(url);
12
+ }
13
+ destroyProvider(provider) {
14
+ provider.destroy();
15
+ }
16
+ async destroyContract(contract) {
17
+ await contract.removeAllListeners();
18
+ }
19
+ connectContract(address, abi, provider) {
20
+ return new ethers_1.Contract(address, abi, provider);
21
+ }
22
+ async erc20Balance(provider, tokenAddress, owner, spender) {
23
+ const tokenContract = this.connectContract(tokenAddress, base_types_1.IERC20_ABI, provider);
24
+ const [balance, gatewayAllowance] = await Promise.all([
25
+ tokenContract.balanceOf(owner),
26
+ tokenContract.allowance(owner, spender),
27
+ ]);
28
+ return { balance, gatewayAllowance };
29
+ }
30
+ async gatewayV1SendToken(provider, gatewayAddress, sender, token, destinationChain, destinationAddress, destinationFee, amount, value) {
31
+ const gateway = this.connectContract(gatewayAddress, base_types_1.IGATEWAY_V1_ABI, provider);
32
+ return await gateway
33
+ .getFunction("sendToken")
34
+ .populateTransaction(token, destinationChain, destinationAddress, destinationFee, amount, { value, from: sender });
35
+ }
36
+ async gatewayV2RegisterToken(provider, gatewayAddress, sender, token, network, executionFee, relayerFee, value) {
37
+ const gateway = this.connectContract(gatewayAddress, base_types_1.IGATEWAY_V2_ABI, provider);
38
+ return await gateway
39
+ .getFunction("v2_registerToken")
40
+ .populateTransaction(token, network, executionFee, relayerFee, {
41
+ value,
42
+ from: sender,
43
+ });
44
+ }
45
+ async gatewayV2CreateAgent(provider, gatewayAddress, id) {
46
+ const gateway = this.connectContract(gatewayAddress, base_types_1.IGATEWAY_V2_ABI, provider);
47
+ return await gateway.getFunction("v2_createAgent").populateTransaction(id);
48
+ }
49
+ async gatewayV2SendMessage(provider, gatewayAddress, sender, xcm, assets, claimer, executionFee, relayerFee, value) {
50
+ const gateway = this.connectContract(gatewayAddress, base_types_1.IGATEWAY_V2_ABI, provider);
51
+ return await gateway
52
+ .getFunction("v2_sendMessage")
53
+ .populateTransaction(xcm, assets, claimer, executionFee, relayerFee, {
54
+ value,
55
+ from: sender,
56
+ });
57
+ }
58
+ async l2AdapterSendEtherAndCall(provider, adapterAddress, sender, params, sendParams, recipient, topic, value) {
59
+ const adapter = this.connectContract(adapterAddress, base_types_1.SNOWBRIDGE_L2_ADAPTOR_ABI, provider);
60
+ const txOptions = value === undefined ? { from: sender } : { from: sender, value };
61
+ return await adapter
62
+ .getFunction("sendEtherAndCall")
63
+ .populateTransaction(params, sendParams, recipient, topic, txOptions);
64
+ }
65
+ async l2AdapterSendTokenAndCall(provider, adapterAddress, sender, params, swapParams, sendParams, recipient, topic) {
66
+ const adapter = this.connectContract(adapterAddress, base_types_1.SNOWBRIDGE_L2_ADAPTOR_ABI, provider);
67
+ return await adapter
68
+ .getFunction("sendTokenAndCall")
69
+ .populateTransaction(params, swapParams, sendParams, recipient, topic, {
70
+ from: sender,
71
+ });
72
+ }
73
+ async evmParachainTransferAssetsUsingTypeAndThenAddress(provider, precompileAddress, sourceAccount, destination, assets, assetsTransferType, remoteFeesIdIndex, feesTransferType, customXcmHex) {
74
+ const precompile = this.connectContract(precompileAddress, base_types_1.MOONBEAM_PALLET_XCM_PRECOMPILE_ABI, provider);
75
+ const tx = await precompile
76
+ .getFunction("transferAssetsUsingTypeAndThenAddress((uint8,bytes[]),(address,uint256)[],uint8,uint8,uint8,bytes)")
77
+ .populateTransaction(destination, assets, assetsTransferType, remoteFeesIdIndex, feesTransferType, customXcmHex);
78
+ tx.from = sourceAccount;
79
+ return tx;
80
+ }
81
+ encodeFunctionData(abi, method, args) {
82
+ return new ethers_1.Interface(abi).encodeFunctionData(method, args);
83
+ }
84
+ decodeFunctionResult(abi, method, data) {
85
+ return new ethers_1.Interface(abi).decodeFunctionResult(method, data);
86
+ }
87
+ encodeNativeAsset(tokenAddress, amount) {
88
+ return ethers_1.AbiCoder.defaultAbiCoder().encode(["uint8", "address", "uint128"], [0, tokenAddress, amount]);
89
+ }
90
+ l1AdapterDepositNativeEther(params, recipient, topic) {
91
+ return new ethers_1.Interface(base_types_1.SNOWBRIDGE_L1_ADAPTOR_ABI).encodeFunctionData("depositNativeEther", [params, recipient, topic]);
92
+ }
93
+ l1AdapterDepositToken(params, recipient, topic) {
94
+ return new ethers_1.Interface(base_types_1.SNOWBRIDGE_L1_ADAPTOR_ABI).encodeFunctionData("depositToken", [params, recipient, topic]);
95
+ }
96
+ l1SwapRouterExactOutputSingle(params) {
97
+ return new ethers_1.Interface(base_types_1.SWAP_ROUTER_ABI).encodeFunctionData("exactOutputSingle", [params]);
98
+ }
99
+ l1LegacySwapRouterExactOutputSingle(params) {
100
+ return new ethers_1.Interface(base_types_1.SWAP_LEGACY_ROUTER_ABI).encodeFunctionData("exactOutputSingle", [params]);
101
+ }
102
+ beneficiaryMultiAddress(beneficiary) {
103
+ const abi = ethers_1.AbiCoder.defaultAbiCoder();
104
+ let kind;
105
+ if (/^0x[a-fA-F0-9]{40}$/.test(beneficiary)) {
106
+ kind = 2;
107
+ }
108
+ else if (/^0x[a-fA-F0-9]{64}$/.test(beneficiary)) {
109
+ kind = 1;
110
+ }
111
+ else {
112
+ throw new Error("Unknown Beneficiary address format.");
113
+ }
114
+ let data;
115
+ switch (kind) {
116
+ case 1:
117
+ data = abi.encode(["bytes32"], [beneficiary]);
118
+ break;
119
+ case 2:
120
+ data = abi.encode(["bytes20"], [beneficiary]);
121
+ break;
122
+ default:
123
+ throw new Error(`Unknown Beneficiary kind {kind}.`);
124
+ }
125
+ return { kind, data };
126
+ }
127
+ async estimateGas(provider, tx) {
128
+ return await provider.estimateGas(tx);
129
+ }
130
+ async getTransactionCount(provider, address, blockTag = "latest") {
131
+ return await provider.getTransactionCount(address, blockTag);
132
+ }
133
+ async getBalance(provider, address) {
134
+ return await provider.getBalance(address);
135
+ }
136
+ async getFeeData(provider) {
137
+ const feeData = await provider.getFeeData();
138
+ return {
139
+ gasPrice: feeData.gasPrice ?? null,
140
+ maxFeePerGas: feeData.maxFeePerGas ?? null,
141
+ maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? null,
142
+ };
143
+ }
144
+ parseUnits(value, decimals) {
145
+ return (0, ethers_1.parseUnits)(value, decimals);
146
+ }
147
+ async gatewayOperatingMode(gateway) {
148
+ return BigInt(await gateway.operatingMode());
149
+ }
150
+ async gatewayChannelOperatingModeOf(gateway, channelId) {
151
+ return BigInt(await gateway.channelOperatingModeOf(channelId));
152
+ }
153
+ async isContractAddress(provider, address) {
154
+ if (!/^0x[a-fA-F0-9]{40}$/.test(address)) {
155
+ return false;
156
+ }
157
+ try {
158
+ const code = await provider.getCode(address);
159
+ return code !== "0x";
160
+ }
161
+ catch {
162
+ return false;
163
+ }
164
+ }
165
+ scanGatewayV1OutboundMessageAccepted(receipt) {
166
+ for (const log of receipt.logs) {
167
+ try {
168
+ const event = EthersEthereumProvider.gatewayV1Interface.parseLog({
169
+ topics: [...log.topics],
170
+ data: log.data,
171
+ });
172
+ if (event && event.name === "OutboundMessageAccepted") {
173
+ return {
174
+ channelId: String(event.args[0]),
175
+ nonce: BigInt(event.args[1]),
176
+ messageId: String(event.args[2]),
177
+ blockHash: receipt.blockHash,
178
+ blockNumber: receipt.blockNumber,
179
+ txHash: receipt.hash,
180
+ txIndex: receipt.index,
181
+ };
182
+ }
183
+ }
184
+ catch { }
185
+ }
186
+ return null;
187
+ }
188
+ scanGatewayV2OutboundMessageAccepted(receipt) {
189
+ for (const log of receipt.logs) {
190
+ try {
191
+ const event = EthersEthereumProvider.gatewayV2Interface.parseLog({
192
+ topics: [...log.topics],
193
+ data: log.data,
194
+ });
195
+ if (event && event.name === "OutboundMessageAccepted") {
196
+ const payload = event.args[1];
197
+ return {
198
+ nonce: BigInt(event.args[0]),
199
+ payload: {
200
+ origin: String(payload.origin),
201
+ assets: payload.assets.map((asset) => [
202
+ Number(asset.kind),
203
+ String(asset.data),
204
+ ]),
205
+ xcm: [Number(payload.xcm.kind), String(payload.xcm.data)],
206
+ claimer: String(payload.claimer),
207
+ value: BigInt(payload.value),
208
+ executionFee: BigInt(payload.executionFee),
209
+ relayerFee: BigInt(payload.relayerFee),
210
+ },
211
+ blockHash: receipt.blockHash,
212
+ blockNumber: receipt.blockNumber,
213
+ txHash: receipt.hash,
214
+ txIndex: receipt.index,
215
+ };
216
+ }
217
+ }
218
+ catch { }
219
+ }
220
+ return null;
221
+ }
222
+ }
223
+ exports.EthersEthereumProvider = EthersEthereumProvider;
224
+ EthersEthereumProvider.gatewayV1Interface = new ethers_1.Interface(base_types_1.IGATEWAY_V1_ABI);
225
+ EthersEthereumProvider.gatewayV2Interface = new ethers_1.Interface(base_types_1.IGATEWAY_V2_ABI);
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@snowbridge/provider-ethers",
3
+ "version": "1.0.0",
4
+ "description": "Snowbridge ethers provider implementation",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/Snowfork/snowbridge.git",
9
+ "directory": "web/packages/provider-ethers"
10
+ },
11
+ "main": "dist/index.js",
12
+ "types": "dist/index.d.ts",
13
+ "devDependencies": {
14
+ "@types/node": "24.6.2",
15
+ "@typescript-eslint/eslint-plugin": "8.45.0",
16
+ "@typescript-eslint/parser": "8.45.0",
17
+ "eslint": "9.37.0",
18
+ "eslint-config-prettier": "10.1.8",
19
+ "prettier": "3.6.2",
20
+ "ts-node": "10.9.2",
21
+ "tsconfig-paths": "4.2.0",
22
+ "typescript": "5.9.3"
23
+ },
24
+ "dependencies": {
25
+ "ethers": "6.16.0",
26
+ "@snowbridge/base-types": "1.0.0"
27
+ },
28
+ "scripts": {
29
+ "build": "tsc --build --force",
30
+ "lint": "eslint .",
31
+ "format": "prettier src --write"
32
+ }
33
+ }
package/src/index.ts ADDED
@@ -0,0 +1,474 @@
1
+ import {
2
+ AbiCoder,
3
+ AbstractProvider,
4
+ Contract,
5
+ ContractTransaction,
6
+ FeeData as EthersFeeData,
7
+ Interface,
8
+ InterfaceAbi,
9
+ JsonRpcProvider,
10
+ parseUnits,
11
+ TransactionReceipt,
12
+ WebSocketProvider,
13
+ } from "ethers";
14
+ import type {
15
+ BeefyClient,
16
+ DepositParamsStruct,
17
+ EthereumProvider,
18
+ EthereumProviderTypes,
19
+ FeeData,
20
+ GatewayV1OutboundMessageAccepted,
21
+ GatewayV2OutboundMessageAccepted,
22
+ IGatewayV1,
23
+ IGatewayV2,
24
+ IERC20,
25
+ L1AdapterDepositParams,
26
+ L1LegacySwapRouterExactOutputSingleParams,
27
+ L1SwapRouterExactOutputSingleParams,
28
+ MultiAddressStruct,
29
+ SendParamsStruct,
30
+ SwapParamsStruct,
31
+ } from "@snowbridge/base-types";
32
+ import {
33
+ IERC20_ABI,
34
+ IGATEWAY_V1_ABI,
35
+ IGATEWAY_V2_ABI,
36
+ MOONBEAM_PALLET_XCM_PRECOMPILE_ABI,
37
+ SNOWBRIDGE_L1_ADAPTOR_ABI,
38
+ SNOWBRIDGE_L2_ADAPTOR_ABI,
39
+ SWAP_LEGACY_ROUTER_ABI,
40
+ SWAP_ROUTER_ABI,
41
+ } from "@snowbridge/base-types";
42
+
43
+ export interface EthersProviderTypes extends EthereumProviderTypes {
44
+ Connection: AbstractProvider;
45
+ Contract: Contract;
46
+ Abi: InterfaceAbi;
47
+ TransactionReceipt: TransactionReceipt;
48
+ ContractTransaction: ContractTransaction;
49
+ }
50
+
51
+ export class EthersEthereumProvider
52
+ implements EthereumProvider<EthersProviderTypes>
53
+ {
54
+ declare readonly providerTypes: EthersProviderTypes;
55
+
56
+ static gatewayV1Interface = new Interface(IGATEWAY_V1_ABI);
57
+ static gatewayV2Interface = new Interface(IGATEWAY_V2_ABI);
58
+
59
+ createProvider(url: string): AbstractProvider {
60
+ if (url.startsWith("http")) {
61
+ return new JsonRpcProvider(url);
62
+ }
63
+ return new WebSocketProvider(url);
64
+ }
65
+
66
+ destroyProvider(provider: AbstractProvider): void {
67
+ provider.destroy();
68
+ }
69
+
70
+ async destroyContract(contract: Contract): Promise<void> {
71
+ await contract.removeAllListeners();
72
+ }
73
+
74
+ connectContract(
75
+ address: string,
76
+ abi: InterfaceAbi,
77
+ provider: AbstractProvider,
78
+ ): Contract {
79
+ return new Contract(address, abi, provider);
80
+ }
81
+
82
+ async erc20Balance(
83
+ provider: AbstractProvider,
84
+ tokenAddress: string,
85
+ owner: string,
86
+ spender: string,
87
+ ): Promise<{ balance: bigint; gatewayAllowance: bigint }> {
88
+ const tokenContract = this.connectContract(
89
+ tokenAddress,
90
+ IERC20_ABI,
91
+ provider,
92
+ ) as Contract & IERC20;
93
+ const [balance, gatewayAllowance] = await Promise.all([
94
+ tokenContract.balanceOf(owner),
95
+ tokenContract.allowance(owner, spender),
96
+ ]);
97
+ return { balance, gatewayAllowance };
98
+ }
99
+
100
+ async gatewayV1SendToken(
101
+ provider: AbstractProvider,
102
+ gatewayAddress: string,
103
+ sender: string,
104
+ token: string,
105
+ destinationChain: number,
106
+ destinationAddress: MultiAddressStruct,
107
+ destinationFee: bigint,
108
+ amount: bigint,
109
+ value: bigint,
110
+ ): Promise<ContractTransaction> {
111
+ const gateway = this.connectContract(
112
+ gatewayAddress,
113
+ IGATEWAY_V1_ABI,
114
+ provider,
115
+ );
116
+ return await gateway
117
+ .getFunction("sendToken")
118
+ .populateTransaction(
119
+ token,
120
+ destinationChain,
121
+ destinationAddress,
122
+ destinationFee,
123
+ amount,
124
+ { value, from: sender },
125
+ );
126
+ }
127
+
128
+ async gatewayV2RegisterToken(
129
+ provider: AbstractProvider,
130
+ gatewayAddress: string,
131
+ sender: string,
132
+ token: string,
133
+ network: number,
134
+ executionFee: bigint,
135
+ relayerFee: bigint,
136
+ value: bigint,
137
+ ): Promise<ContractTransaction> {
138
+ const gateway = this.connectContract(
139
+ gatewayAddress,
140
+ IGATEWAY_V2_ABI,
141
+ provider,
142
+ );
143
+ return await gateway
144
+ .getFunction("v2_registerToken")
145
+ .populateTransaction(token, network, executionFee, relayerFee, {
146
+ value,
147
+ from: sender,
148
+ });
149
+ }
150
+
151
+ async gatewayV2CreateAgent(
152
+ provider: AbstractProvider,
153
+ gatewayAddress: string,
154
+ id: string,
155
+ ): Promise<ContractTransaction> {
156
+ const gateway = this.connectContract(
157
+ gatewayAddress,
158
+ IGATEWAY_V2_ABI,
159
+ provider,
160
+ );
161
+ return await gateway.getFunction("v2_createAgent").populateTransaction(id);
162
+ }
163
+
164
+ async gatewayV2SendMessage(
165
+ provider: AbstractProvider,
166
+ gatewayAddress: string,
167
+ sender: string,
168
+ xcm: Uint8Array,
169
+ assets: string[],
170
+ claimer: Uint8Array,
171
+ executionFee: bigint,
172
+ relayerFee: bigint,
173
+ value: bigint,
174
+ ): Promise<ContractTransaction> {
175
+ const gateway = this.connectContract(
176
+ gatewayAddress,
177
+ IGATEWAY_V2_ABI,
178
+ provider,
179
+ );
180
+ return await gateway
181
+ .getFunction("v2_sendMessage")
182
+ .populateTransaction(xcm, assets, claimer, executionFee, relayerFee, {
183
+ value,
184
+ from: sender,
185
+ });
186
+ }
187
+
188
+ async l2AdapterSendEtherAndCall(
189
+ provider: AbstractProvider,
190
+ adapterAddress: string,
191
+ sender: string,
192
+ params: DepositParamsStruct,
193
+ sendParams: SendParamsStruct,
194
+ recipient: string,
195
+ topic: string,
196
+ value?: bigint,
197
+ ): Promise<ContractTransaction> {
198
+ const adapter = this.connectContract(
199
+ adapterAddress,
200
+ SNOWBRIDGE_L2_ADAPTOR_ABI,
201
+ provider,
202
+ );
203
+ const txOptions =
204
+ value === undefined ? { from: sender } : { from: sender, value };
205
+ return await adapter
206
+ .getFunction("sendEtherAndCall")
207
+ .populateTransaction(params, sendParams, recipient, topic, txOptions);
208
+ }
209
+
210
+ async l2AdapterSendTokenAndCall(
211
+ provider: AbstractProvider,
212
+ adapterAddress: string,
213
+ sender: string,
214
+ params: DepositParamsStruct,
215
+ swapParams: SwapParamsStruct,
216
+ sendParams: SendParamsStruct,
217
+ recipient: string,
218
+ topic: string,
219
+ ): Promise<ContractTransaction> {
220
+ const adapter = this.connectContract(
221
+ adapterAddress,
222
+ SNOWBRIDGE_L2_ADAPTOR_ABI,
223
+ provider,
224
+ );
225
+ return await adapter
226
+ .getFunction("sendTokenAndCall")
227
+ .populateTransaction(params, swapParams, sendParams, recipient, topic, {
228
+ from: sender,
229
+ });
230
+ }
231
+
232
+ async evmParachainTransferAssetsUsingTypeAndThenAddress(
233
+ provider: AbstractProvider,
234
+ precompileAddress: string,
235
+ sourceAccount: string,
236
+ destination: [number, string[]],
237
+ assets: [string, bigint][],
238
+ assetsTransferType: number,
239
+ remoteFeesIdIndex: number,
240
+ feesTransferType: number,
241
+ customXcmHex: string,
242
+ ): Promise<ContractTransaction> {
243
+ const precompile = this.connectContract(
244
+ precompileAddress,
245
+ MOONBEAM_PALLET_XCM_PRECOMPILE_ABI,
246
+ provider,
247
+ );
248
+ const tx = await precompile
249
+ .getFunction(
250
+ "transferAssetsUsingTypeAndThenAddress((uint8,bytes[]),(address,uint256)[],uint8,uint8,uint8,bytes)",
251
+ )
252
+ .populateTransaction(
253
+ destination,
254
+ assets,
255
+ assetsTransferType,
256
+ remoteFeesIdIndex,
257
+ feesTransferType,
258
+ customXcmHex,
259
+ );
260
+ tx.from = sourceAccount;
261
+ return tx;
262
+ }
263
+
264
+ encodeFunctionData(
265
+ abi: InterfaceAbi,
266
+ method: string,
267
+ args: readonly unknown[],
268
+ ): string {
269
+ return new Interface(abi).encodeFunctionData(method, args);
270
+ }
271
+
272
+ decodeFunctionResult<T = unknown>(
273
+ abi: InterfaceAbi,
274
+ method: string,
275
+ data: string,
276
+ ): T {
277
+ return new Interface(abi).decodeFunctionResult(method, data) as T;
278
+ }
279
+
280
+ encodeNativeAsset(tokenAddress: string, amount: bigint): string {
281
+ return AbiCoder.defaultAbiCoder().encode(
282
+ ["uint8", "address", "uint128"],
283
+ [0, tokenAddress, amount],
284
+ );
285
+ }
286
+
287
+ l1AdapterDepositNativeEther(
288
+ params: L1AdapterDepositParams,
289
+ recipient: string,
290
+ topic: string,
291
+ ): string {
292
+ return new Interface(SNOWBRIDGE_L1_ADAPTOR_ABI).encodeFunctionData(
293
+ "depositNativeEther",
294
+ [params, recipient, topic],
295
+ );
296
+ }
297
+
298
+ l1AdapterDepositToken(
299
+ params: L1AdapterDepositParams,
300
+ recipient: string,
301
+ topic: string,
302
+ ): string {
303
+ return new Interface(SNOWBRIDGE_L1_ADAPTOR_ABI).encodeFunctionData(
304
+ "depositToken",
305
+ [params, recipient, topic],
306
+ );
307
+ }
308
+
309
+ l1SwapRouterExactOutputSingle(
310
+ params: L1SwapRouterExactOutputSingleParams,
311
+ ): string {
312
+ return new Interface(SWAP_ROUTER_ABI).encodeFunctionData(
313
+ "exactOutputSingle",
314
+ [params],
315
+ );
316
+ }
317
+
318
+ l1LegacySwapRouterExactOutputSingle(
319
+ params: L1LegacySwapRouterExactOutputSingleParams,
320
+ ): string {
321
+ return new Interface(SWAP_LEGACY_ROUTER_ABI).encodeFunctionData(
322
+ "exactOutputSingle",
323
+ [params],
324
+ );
325
+ }
326
+
327
+ beneficiaryMultiAddress(beneficiary: string): MultiAddressStruct {
328
+ const abi = AbiCoder.defaultAbiCoder();
329
+ let kind: number;
330
+ if (/^0x[a-fA-F0-9]{40}$/.test(beneficiary)) {
331
+ kind = 2;
332
+ } else if (/^0x[a-fA-F0-9]{64}$/.test(beneficiary)) {
333
+ kind = 1;
334
+ } else {
335
+ throw new Error("Unknown Beneficiary address format.");
336
+ }
337
+ let data: string;
338
+ switch (kind) {
339
+ case 1:
340
+ data = abi.encode(["bytes32"], [beneficiary]);
341
+ break;
342
+ case 2:
343
+ data = abi.encode(["bytes20"], [beneficiary]);
344
+ break;
345
+ default:
346
+ throw new Error(`Unknown Beneficiary kind {kind}.`);
347
+ }
348
+ return { kind, data };
349
+ }
350
+
351
+ async estimateGas(
352
+ provider: AbstractProvider,
353
+ tx: ContractTransaction,
354
+ ): Promise<bigint> {
355
+ return await provider.estimateGas(tx);
356
+ }
357
+
358
+ async getTransactionCount(
359
+ provider: AbstractProvider,
360
+ address: string,
361
+ blockTag: "latest" | "pending" = "latest",
362
+ ): Promise<number> {
363
+ return await provider.getTransactionCount(address, blockTag);
364
+ }
365
+
366
+ async getBalance(
367
+ provider: AbstractProvider,
368
+ address: string,
369
+ ): Promise<bigint> {
370
+ return await provider.getBalance(address);
371
+ }
372
+
373
+ async getFeeData(provider: AbstractProvider): Promise<FeeData> {
374
+ const feeData: EthersFeeData = await provider.getFeeData();
375
+ return {
376
+ gasPrice: feeData.gasPrice ?? null,
377
+ maxFeePerGas: feeData.maxFeePerGas ?? null,
378
+ maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? null,
379
+ };
380
+ }
381
+
382
+ parseUnits(value: string, decimals: number): bigint {
383
+ return parseUnits(value, decimals);
384
+ }
385
+
386
+ async gatewayOperatingMode(
387
+ gateway: Contract & (IGatewayV1 | IGatewayV2),
388
+ ): Promise<bigint> {
389
+ return BigInt(await gateway.operatingMode());
390
+ }
391
+
392
+ async gatewayChannelOperatingModeOf(
393
+ gateway: Contract & IGatewayV1,
394
+ channelId: string,
395
+ ): Promise<bigint> {
396
+ return BigInt(await gateway.channelOperatingModeOf(channelId));
397
+ }
398
+
399
+ async isContractAddress(
400
+ provider: AbstractProvider,
401
+ address: string,
402
+ ): Promise<boolean> {
403
+ if (!/^0x[a-fA-F0-9]{40}$/.test(address)) {
404
+ return false;
405
+ }
406
+ try {
407
+ const code = await provider.getCode(address);
408
+ return code !== "0x";
409
+ } catch {
410
+ return false;
411
+ }
412
+ }
413
+
414
+ scanGatewayV1OutboundMessageAccepted(
415
+ receipt: TransactionReceipt,
416
+ ): GatewayV1OutboundMessageAccepted | null {
417
+ for (const log of receipt.logs) {
418
+ try {
419
+ const event = EthersEthereumProvider.gatewayV1Interface.parseLog({
420
+ topics: [...log.topics],
421
+ data: log.data,
422
+ });
423
+ if (event && event.name === "OutboundMessageAccepted") {
424
+ return {
425
+ channelId: String(event.args[0]),
426
+ nonce: BigInt(event.args[1]),
427
+ messageId: String(event.args[2]),
428
+ blockHash: receipt.blockHash,
429
+ blockNumber: receipt.blockNumber,
430
+ txHash: receipt.hash,
431
+ txIndex: receipt.index,
432
+ };
433
+ }
434
+ } catch {}
435
+ }
436
+ return null;
437
+ }
438
+
439
+ scanGatewayV2OutboundMessageAccepted(
440
+ receipt: TransactionReceipt,
441
+ ): GatewayV2OutboundMessageAccepted | null {
442
+ for (const log of receipt.logs) {
443
+ try {
444
+ const event = EthersEthereumProvider.gatewayV2Interface.parseLog({
445
+ topics: [...log.topics],
446
+ data: log.data,
447
+ });
448
+ if (event && event.name === "OutboundMessageAccepted") {
449
+ const payload = event.args[1];
450
+ return {
451
+ nonce: BigInt(event.args[0]),
452
+ payload: {
453
+ origin: String(payload.origin),
454
+ assets: payload.assets.map((asset: MultiAddressStruct) => [
455
+ Number(asset.kind),
456
+ String(asset.data),
457
+ ]),
458
+ xcm: [Number(payload.xcm.kind), String(payload.xcm.data)],
459
+ claimer: String(payload.claimer),
460
+ value: BigInt(payload.value),
461
+ executionFee: BigInt(payload.executionFee),
462
+ relayerFee: BigInt(payload.relayerFee),
463
+ },
464
+ blockHash: receipt.blockHash,
465
+ blockNumber: receipt.blockNumber,
466
+ txHash: receipt.hash,
467
+ txIndex: receipt.index,
468
+ };
469
+ }
470
+ } catch {}
471
+ }
472
+ return null;
473
+ }
474
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "target": "es2021",
5
+ "module": "commonjs",
6
+ "strict": true,
7
+ "resolveJsonModule": true,
8
+ "esModuleInterop": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "outDir": "dist",
11
+ "allowSyntheticDefaultImports": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "baseUrl": ".",
15
+ "rootDir": "src",
16
+ "noEmitOnError": true,
17
+ "skipLibCheck": true,
18
+ "allowJs": true
19
+ },
20
+ "ts-node": {
21
+ "require": ["tsconfig-paths/register"]
22
+ },
23
+ "exclude": ["node_modules", "dist"],
24
+ "include": ["src/**/*.ts"],
25
+ "references": []
26
+ }