@0xsequence/relayer 2.3.39 → 3.0.0-beta.10
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/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +3926 -0
- package/LICENSE +0 -17
- package/README.md +1 -2
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/preconditions/codec.d.ts +12 -0
- package/dist/preconditions/codec.d.ts.map +1 -0
- package/dist/preconditions/codec.js +125 -0
- package/dist/preconditions/index.d.ts +4 -0
- package/dist/preconditions/index.d.ts.map +1 -0
- package/dist/preconditions/index.js +3 -0
- package/dist/preconditions/selectors.d.ts +7 -0
- package/dist/preconditions/selectors.d.ts.map +1 -0
- package/dist/preconditions/selectors.js +27 -0
- package/dist/preconditions/types.d.ts +70 -0
- package/dist/preconditions/types.d.ts.map +1 -0
- package/dist/preconditions/types.js +203 -0
- package/dist/relayer/index.d.ts +45 -0
- package/dist/relayer/index.d.ts.map +1 -0
- package/dist/relayer/index.js +3 -0
- package/dist/relayer/relayer.d.ts +26 -0
- package/dist/relayer/relayer.d.ts.map +1 -0
- package/dist/relayer/relayer.js +7 -0
- package/dist/relayer/rpc-relayer/index.d.ts +38 -0
- package/dist/relayer/rpc-relayer/index.d.ts.map +1 -0
- package/dist/relayer/rpc-relayer/index.js +375 -0
- package/dist/{declarations/src → relayer}/rpc-relayer/relayer.gen.d.ts +3 -2
- package/dist/relayer/rpc-relayer/relayer.gen.d.ts.map +1 -0
- package/dist/relayer/rpc-relayer/relayer.gen.js +1246 -0
- package/dist/relayer/standard/abi.d.ts +73 -0
- package/dist/relayer/standard/abi.d.ts.map +1 -0
- package/dist/relayer/standard/abi.js +10 -0
- package/dist/relayer/standard/eip6963.d.ts +31 -0
- package/dist/relayer/standard/eip6963.d.ts.map +1 -0
- package/dist/relayer/standard/eip6963.js +51 -0
- package/dist/relayer/standard/index.d.ts +5 -0
- package/dist/relayer/standard/index.d.ts.map +1 -0
- package/dist/relayer/standard/index.js +4 -0
- package/dist/relayer/standard/local.d.ts +60 -0
- package/dist/relayer/standard/local.d.ts.map +1 -0
- package/dist/relayer/standard/local.js +285 -0
- package/dist/relayer/standard/pk-relayer.d.ts +28 -0
- package/dist/relayer/standard/pk-relayer.d.ts.map +1 -0
- package/dist/relayer/standard/pk-relayer.js +112 -0
- package/dist/relayer/standard/sequence.d.ts +27 -0
- package/dist/relayer/standard/sequence.d.ts.map +1 -0
- package/dist/relayer/standard/sequence.js +84 -0
- package/package.json +28 -25
- package/src/index.ts +3 -111
- package/src/preconditions/codec.ts +190 -0
- package/src/preconditions/index.ts +3 -0
- package/src/preconditions/selectors.ts +38 -0
- package/src/preconditions/types.ts +201 -0
- package/src/relayer/index.ts +60 -0
- package/src/relayer/relayer.ts +37 -0
- package/src/relayer/rpc-relayer/index.ts +449 -0
- package/src/{rpc-relayer → relayer/rpc-relayer}/relayer.gen.ts +483 -258
- package/src/relayer/standard/abi.ts +13 -0
- package/src/relayer/standard/eip6963.ts +74 -0
- package/src/relayer/standard/index.ts +4 -0
- package/src/relayer/standard/local.ts +353 -0
- package/src/relayer/standard/pk-relayer.ts +138 -0
- package/src/relayer/standard/sequence.ts +110 -0
- package/test/preconditions/codec.test.ts +531 -0
- package/test/preconditions/preconditions.test.ts +283 -0
- package/test/preconditions/selectors.test.ts +415 -0
- package/test/preconditions/types.test.ts +443 -0
- package/test/relayer/relayer.test.ts +355 -0
- package/tsconfig.json +10 -0
- package/dist/0xsequence-relayer.cjs.d.ts +0 -2
- package/dist/0xsequence-relayer.cjs.dev.js +0 -1865
- package/dist/0xsequence-relayer.cjs.js +0 -7
- package/dist/0xsequence-relayer.cjs.prod.js +0 -1865
- package/dist/0xsequence-relayer.esm.js +0 -1852
- package/dist/declarations/src/index.d.ts +0 -42
- package/dist/declarations/src/local-relayer.d.ts +0 -35
- package/dist/declarations/src/provider-relayer.d.ts +0 -47
- package/dist/declarations/src/rpc-relayer/index.d.ts +0 -72
- package/src/local-relayer.ts +0 -125
- package/src/provider-relayer.ts +0 -284
- package/src/rpc-relayer/index.ts +0 -380
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Address, Hex, Secp256k1, TransactionEnvelopeEip1559, TransactionReceipt } from 'ox';
|
|
2
|
+
import { LocalRelayer } from './local.js';
|
|
3
|
+
export class PkRelayer {
|
|
4
|
+
provider;
|
|
5
|
+
kind = 'relayer';
|
|
6
|
+
type = 'pk';
|
|
7
|
+
id = 'pk';
|
|
8
|
+
relayer;
|
|
9
|
+
constructor(privateKey, provider) {
|
|
10
|
+
this.provider = provider;
|
|
11
|
+
const relayerAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey }));
|
|
12
|
+
this.relayer = new LocalRelayer({
|
|
13
|
+
sendTransaction: async (args, chainId) => {
|
|
14
|
+
const providerChainId = Number(await this.provider.request({ method: 'eth_chainId' }));
|
|
15
|
+
if (providerChainId !== chainId) {
|
|
16
|
+
throw new Error('Provider chain id does not match relayer chain id');
|
|
17
|
+
}
|
|
18
|
+
const oxArgs = { ...args, to: args.to, data: args.data };
|
|
19
|
+
// Estimate gas with a safety buffer
|
|
20
|
+
const estimatedGas = BigInt(await this.provider.request({ method: 'eth_estimateGas', params: [oxArgs] }));
|
|
21
|
+
const safeGasLimit = estimatedGas > 21000n ? (estimatedGas * 12n) / 10n : 50000n;
|
|
22
|
+
// Get base fee and priority fee
|
|
23
|
+
const baseFee = BigInt(await this.provider.request({ method: 'eth_gasPrice' }));
|
|
24
|
+
const priorityFee = 100000000n; // 0.1 gwei priority fee
|
|
25
|
+
const maxFeePerGas = baseFee + priorityFee;
|
|
26
|
+
// Check sender have enough balance
|
|
27
|
+
const senderBalance = BigInt(await this.provider.request({ method: 'eth_getBalance', params: [relayerAddress, 'latest'] }));
|
|
28
|
+
if (senderBalance < maxFeePerGas * safeGasLimit) {
|
|
29
|
+
console.log('Sender balance:', senderBalance.toString(), 'wei');
|
|
30
|
+
throw new Error('Sender has insufficient balance to pay for gas');
|
|
31
|
+
}
|
|
32
|
+
const nonce = BigInt(await this.provider.request({
|
|
33
|
+
method: 'eth_getTransactionCount',
|
|
34
|
+
params: [relayerAddress, 'latest'],
|
|
35
|
+
}));
|
|
36
|
+
// Build the relay envelope
|
|
37
|
+
const relayEnvelope = TransactionEnvelopeEip1559.from({
|
|
38
|
+
chainId: Number(chainId),
|
|
39
|
+
type: 'eip1559',
|
|
40
|
+
from: relayerAddress,
|
|
41
|
+
to: oxArgs.to,
|
|
42
|
+
data: oxArgs.data,
|
|
43
|
+
gas: safeGasLimit,
|
|
44
|
+
maxFeePerGas: maxFeePerGas,
|
|
45
|
+
maxPriorityFeePerGas: priorityFee,
|
|
46
|
+
nonce: nonce,
|
|
47
|
+
value: 0n,
|
|
48
|
+
});
|
|
49
|
+
const relayerSignature = Secp256k1.sign({
|
|
50
|
+
payload: TransactionEnvelopeEip1559.getSignPayload(relayEnvelope),
|
|
51
|
+
privateKey: privateKey,
|
|
52
|
+
});
|
|
53
|
+
const signedRelayEnvelope = TransactionEnvelopeEip1559.from(relayEnvelope, {
|
|
54
|
+
signature: relayerSignature,
|
|
55
|
+
});
|
|
56
|
+
const tx = await this.provider.request({
|
|
57
|
+
method: 'eth_sendRawTransaction',
|
|
58
|
+
params: [TransactionEnvelopeEip1559.serialize(signedRelayEnvelope)],
|
|
59
|
+
});
|
|
60
|
+
return tx;
|
|
61
|
+
},
|
|
62
|
+
getBalance: async (address) => {
|
|
63
|
+
const balanceHex = await this.provider.request({
|
|
64
|
+
method: 'eth_getBalance',
|
|
65
|
+
params: [address, 'latest'],
|
|
66
|
+
});
|
|
67
|
+
return BigInt(balanceHex);
|
|
68
|
+
},
|
|
69
|
+
call: async (args) => {
|
|
70
|
+
const callArgs = { to: args.to, data: args.data };
|
|
71
|
+
return await this.provider.request({ method: 'eth_call', params: [callArgs, 'latest'] });
|
|
72
|
+
},
|
|
73
|
+
getTransactionReceipt: async (txHash, chainId) => {
|
|
74
|
+
Hex.assert(txHash);
|
|
75
|
+
const providerChainId = Number(await this.provider.request({ method: 'eth_chainId' }));
|
|
76
|
+
if (providerChainId !== chainId) {
|
|
77
|
+
throw new Error('Provider chain id does not match relayer chain id');
|
|
78
|
+
}
|
|
79
|
+
const rpcReceipt = await this.provider.request({ method: 'eth_getTransactionReceipt', params: [txHash] });
|
|
80
|
+
if (!rpcReceipt) {
|
|
81
|
+
return 'unknown';
|
|
82
|
+
}
|
|
83
|
+
const receipt = TransactionReceipt.fromRpc(rpcReceipt);
|
|
84
|
+
return receipt.status === 'success' ? 'success' : 'failed';
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async isAvailable(_wallet, chainId) {
|
|
89
|
+
const providerChainId = Number(await this.provider.request({ method: 'eth_chainId' }));
|
|
90
|
+
return providerChainId === chainId;
|
|
91
|
+
}
|
|
92
|
+
feeTokens() {
|
|
93
|
+
return this.relayer.feeTokens();
|
|
94
|
+
}
|
|
95
|
+
feeOptions(wallet, chainId, calls) {
|
|
96
|
+
return this.relayer.feeOptions(wallet, chainId, calls);
|
|
97
|
+
}
|
|
98
|
+
async relay(to, data, chainId, _) {
|
|
99
|
+
const providerChainId = Number(await this.provider.request({ method: 'eth_chainId' }));
|
|
100
|
+
if (providerChainId !== chainId) {
|
|
101
|
+
throw new Error('Provider chain id does not match relayer chain id');
|
|
102
|
+
}
|
|
103
|
+
return this.relayer.relay(to, data, chainId);
|
|
104
|
+
}
|
|
105
|
+
status(opHash, chainId) {
|
|
106
|
+
return this.relayer.status(opHash, chainId);
|
|
107
|
+
}
|
|
108
|
+
async checkPrecondition(precondition) {
|
|
109
|
+
// TODO: Implement precondition check
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { TransactionPrecondition, FeeToken } from '../rpc-relayer/relayer.gen.js';
|
|
2
|
+
import { Payload } from '@0xsequence/wallet-primitives';
|
|
3
|
+
import { Address, Hex } from 'ox';
|
|
4
|
+
import { FeeOption, FeeQuote, OperationStatus, Relayer } from '../index.js';
|
|
5
|
+
export declare class SequenceRelayer implements Relayer {
|
|
6
|
+
readonly kind: 'relayer';
|
|
7
|
+
readonly type = "sequence";
|
|
8
|
+
readonly id = "sequence";
|
|
9
|
+
private readonly service;
|
|
10
|
+
constructor(host: string);
|
|
11
|
+
isAvailable(_wallet: Address.Address, _chainId: number): Promise<boolean>;
|
|
12
|
+
feeTokens(): Promise<{
|
|
13
|
+
isFeeRequired: boolean;
|
|
14
|
+
tokens?: FeeToken[];
|
|
15
|
+
paymentAddress?: Address.Address;
|
|
16
|
+
}>;
|
|
17
|
+
feeOptions(wallet: Address.Address, _chainId: number, calls: Payload.Call[]): Promise<{
|
|
18
|
+
options: FeeOption[];
|
|
19
|
+
quote?: FeeQuote;
|
|
20
|
+
}>;
|
|
21
|
+
checkPrecondition(precondition: TransactionPrecondition): Promise<boolean>;
|
|
22
|
+
relay(to: Address.Address, data: Hex.Hex, _chainId: number, quote?: FeeQuote): Promise<{
|
|
23
|
+
opHash: Hex.Hex;
|
|
24
|
+
}>;
|
|
25
|
+
status(opHash: Hex.Hex, _chainId: number): Promise<OperationStatus>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=sequence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sequence.d.ts","sourceRoot":"","sources":["../../../src/relayer/standard/sequence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,uBAAuB,EAAsB,QAAQ,EAAE,MAAM,+BAA+B,CAAA;AACnH,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAA;AACvD,OAAO,EAAe,OAAO,EAAS,GAAG,EAAE,MAAM,IAAI,CAAA;AACrD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC3E,qBAAa,eAAgB,YAAW,OAAO;IAC7C,SAAgB,IAAI,EAAE,SAAS,CAAY;IAC3C,SAAgB,IAAI,cAAa;IACjC,QAAQ,CAAC,EAAE,cAAa;IAExB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,IAAI,EAAE,MAAM;IAIlB,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzE,SAAS,IAAI,OAAO,CAAC;QAAE,aAAa,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAC,OAAO,CAAA;KAAE,CAAC;IAgBvG,UAAU,CACd,MAAM,EAAE,OAAO,CAAC,OAAO,EACvB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,GACpB,OAAO,CAAC;QAAE,OAAO,EAAE,SAAS,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,QAAQ,CAAA;KAAE,CAAC;IAehD,iBAAiB,CAAC,YAAY,EAAE,uBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC;IAK1E,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,GAAG,CAAC,GAAG,CAAA;KAAE,CAAC;IAW3G,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;CAuC1E"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ETHTxnStatus, Relayer as Service } from '../rpc-relayer/relayer.gen.js';
|
|
2
|
+
import { Payload } from '@0xsequence/wallet-primitives';
|
|
3
|
+
import { AbiFunction, Address, Bytes, Hex } from 'ox';
|
|
4
|
+
export class SequenceRelayer {
|
|
5
|
+
kind = 'relayer';
|
|
6
|
+
type = 'sequence';
|
|
7
|
+
id = 'sequence';
|
|
8
|
+
service;
|
|
9
|
+
constructor(host) {
|
|
10
|
+
this.service = new Service(host, fetch);
|
|
11
|
+
}
|
|
12
|
+
async isAvailable(_wallet, _chainId) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
async feeTokens() {
|
|
16
|
+
const { isFeeRequired, tokens, paymentAddress } = await this.service.feeTokens();
|
|
17
|
+
if (isFeeRequired) {
|
|
18
|
+
Address.assert(paymentAddress);
|
|
19
|
+
return {
|
|
20
|
+
isFeeRequired,
|
|
21
|
+
tokens,
|
|
22
|
+
paymentAddress,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Not required
|
|
26
|
+
return {
|
|
27
|
+
isFeeRequired,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async feeOptions(wallet, _chainId, calls) {
|
|
31
|
+
const to = wallet; // TODO: this might be the guest module
|
|
32
|
+
const execute = AbiFunction.from('function execute(bytes calldata _payload, bytes calldata _signature)');
|
|
33
|
+
const payload = Payload.encode({ type: 'call', space: 0n, nonce: 0n, calls }, to);
|
|
34
|
+
const signature = '0x0001'; // TODO: use a stub signature
|
|
35
|
+
const data = AbiFunction.encodeData(execute, [Bytes.toHex(payload), signature]);
|
|
36
|
+
const { options, quote } = await this.service.feeOptions({ wallet, to, data });
|
|
37
|
+
return {
|
|
38
|
+
options,
|
|
39
|
+
quote: quote ? { _tag: 'FeeQuote', _quote: quote } : undefined,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async checkPrecondition(precondition) {
|
|
43
|
+
// TODO: implement
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
async relay(to, data, _chainId, quote) {
|
|
47
|
+
const walletAddress = to; // TODO: pass wallet address or stop requiring it
|
|
48
|
+
const { txnHash } = await this.service.sendMetaTxn({
|
|
49
|
+
call: { walletAddress, contract: to, input: data },
|
|
50
|
+
quote: quote && quote._quote,
|
|
51
|
+
});
|
|
52
|
+
return { opHash: `0x${txnHash}` };
|
|
53
|
+
}
|
|
54
|
+
async status(opHash, _chainId) {
|
|
55
|
+
try {
|
|
56
|
+
const { receipt: { status, revertReason, txnReceipt }, } = await this.service.getMetaTxnReceipt({ metaTxID: opHash });
|
|
57
|
+
switch (status) {
|
|
58
|
+
case ETHTxnStatus.UNKNOWN:
|
|
59
|
+
return { status: 'unknown' };
|
|
60
|
+
case ETHTxnStatus.DROPPED:
|
|
61
|
+
return { status: 'failed', reason: revertReason ?? status };
|
|
62
|
+
case ETHTxnStatus.QUEUED:
|
|
63
|
+
return { status: 'pending' };
|
|
64
|
+
case ETHTxnStatus.SENT:
|
|
65
|
+
return { status: 'pending' };
|
|
66
|
+
case ETHTxnStatus.SUCCEEDED: {
|
|
67
|
+
const receipt = JSON.parse(txnReceipt);
|
|
68
|
+
const transactionHash = receipt.transactionHash;
|
|
69
|
+
Hex.assert(transactionHash);
|
|
70
|
+
return { status: 'confirmed', transactionHash };
|
|
71
|
+
}
|
|
72
|
+
case ETHTxnStatus.PARTIALLY_FAILED:
|
|
73
|
+
return { status: 'failed', reason: revertReason ?? status };
|
|
74
|
+
case ETHTxnStatus.FAILED:
|
|
75
|
+
return { status: 'failed', reason: revertReason ?? status };
|
|
76
|
+
default:
|
|
77
|
+
throw new Error(`unknown transaction status '${status}'`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return { status: 'pending' };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
package/package.json
CHANGED
|
@@ -1,36 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xsequence/relayer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-beta.10",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
4
8
|
"description": "relayer sub-package for Sequence",
|
|
5
|
-
"repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/relayer",
|
|
6
|
-
"
|
|
7
|
-
"main": "dist/0xsequence-relayer.cjs.js",
|
|
8
|
-
"module": "dist/0xsequence-relayer.esm.js",
|
|
9
|
-
"author": "Horizon Blockchain Games",
|
|
9
|
+
"repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/relayer",
|
|
10
|
+
"author": "Sequence Platforms Inc.",
|
|
10
11
|
"license": "Apache-2.0",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"@0xsequence/core": "2.3.39",
|
|
17
|
-
"@0xsequence/utils": "2.3.39"
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
18
17
|
},
|
|
19
18
|
"devDependencies": {
|
|
20
|
-
"@
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"@
|
|
19
|
+
"@types/node": "^25.0.2",
|
|
20
|
+
"typescript": "^5.9.3",
|
|
21
|
+
"vitest": "^4.0.15",
|
|
22
|
+
"@repo/typescript-config": "^0.0.1-beta.1"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"mipd": "^0.0.7",
|
|
26
|
+
"ox": "^0.9.17",
|
|
27
|
+
"viem": "^2.40.3",
|
|
28
|
+
"@0xsequence/wallet-primitives": "^3.0.0-beta.10"
|
|
24
29
|
},
|
|
25
|
-
"files": [
|
|
26
|
-
"src",
|
|
27
|
-
"dist"
|
|
28
|
-
],
|
|
29
30
|
"scripts": {
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"test:
|
|
33
|
-
"test:
|
|
31
|
+
"build": "tsc",
|
|
32
|
+
"dev": "tsc --watch",
|
|
33
|
+
"old-test": "pnpm test:concurrently 'pnpm test:run'",
|
|
34
|
+
"old-test:run": "pnpm test:file tests/**/*.spec.ts",
|
|
35
|
+
"old-test:file": "NODE_OPTIONS='--import tsx' mocha --timeout 60000",
|
|
36
|
+
"old-test:concurrently": "concurrently -k --success first 'pnpm start:hardhat > /dev/null' ",
|
|
34
37
|
"start:hardhat": "pnpm hardhat node --port 9547",
|
|
35
38
|
"typecheck": "tsc --noEmit"
|
|
36
39
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,111 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { commons } from '@0xsequence/core'
|
|
5
|
-
|
|
6
|
-
export interface Relayer {
|
|
7
|
-
// simulate returns the execution results for a list of transactions.
|
|
8
|
-
simulate(wallet: string, ...transactions: commons.transaction.Transaction[]): Promise<SimulateResult[]>
|
|
9
|
-
|
|
10
|
-
// getFeeOptions returns the fee options that the relayer will accept as payment.
|
|
11
|
-
// If a quote is returned, it may be passed back to the relayer for dispatch.
|
|
12
|
-
getFeeOptions(
|
|
13
|
-
address: string,
|
|
14
|
-
...transactions: commons.transaction.Transaction[]
|
|
15
|
-
): Promise<{ options: FeeOption[]; quote?: FeeQuote }>
|
|
16
|
-
|
|
17
|
-
// getFeeOptionsRaw returns the fee options that the relayer will accept as payment.
|
|
18
|
-
// If a quote is returned, it may be passed back to the relayer for dispatch.
|
|
19
|
-
// It doesn't make any assumptions about the transaction format.
|
|
20
|
-
getFeeOptionsRaw(
|
|
21
|
-
entrypoint: string,
|
|
22
|
-
data: ethers.BytesLike,
|
|
23
|
-
options?: {
|
|
24
|
-
simulate?: boolean
|
|
25
|
-
}
|
|
26
|
-
): Promise<{ options: FeeOption[]; quote?: FeeQuote }>
|
|
27
|
-
|
|
28
|
-
// gasRefundOptions returns the transactions which can be included to refund a
|
|
29
|
-
// relayer for submitting your transaction to a network.
|
|
30
|
-
gasRefundOptions(address: string, ...transactions: commons.transaction.Transaction[]): Promise<FeeOption[]>
|
|
31
|
-
|
|
32
|
-
// Gas tank sponsorship management
|
|
33
|
-
listGasSponsors(args: proto.ListGasSponsorsArgs): Promise<proto.ListGasSponsorsReturn>
|
|
34
|
-
addGasSponsor(args: proto.AddGasSponsorArgs): Promise<proto.AddGasSponsorReturn>
|
|
35
|
-
updateGasSponsor(args: proto.UpdateGasSponsorArgs): Promise<proto.UpdateGasSponsorReturn>
|
|
36
|
-
removeGasSponsor(args: proto.RemoveGasSponsorArgs): Promise<proto.RemoveGasSponsorReturn>
|
|
37
|
-
|
|
38
|
-
// getNonce returns the transaction count/nonce for a wallet, encoded with nonce space.
|
|
39
|
-
// If space is undefined, the relayer can choose a nonce space to encode the result with.
|
|
40
|
-
// Otherwise, the relayer must return a nonce encoded for the given nonce space.
|
|
41
|
-
getNonce(address: string, space?: ethers.BigNumberish, blockTag?: ethers.BlockTag): Promise<ethers.BigNumberish>
|
|
42
|
-
|
|
43
|
-
// relayer will submit the transaction(s) to the network and return the transaction response.
|
|
44
|
-
// The quote should be the one returned from getFeeOptions, if any.
|
|
45
|
-
// waitForReceipt must default to true.
|
|
46
|
-
relay(
|
|
47
|
-
signedTxs: commons.transaction.IntendedTransactionBundle,
|
|
48
|
-
quote?: FeeQuote,
|
|
49
|
-
waitForReceipt?: boolean,
|
|
50
|
-
projectAccessKey?: string
|
|
51
|
-
): Promise<commons.transaction.TransactionResponse>
|
|
52
|
-
|
|
53
|
-
// wait for transaction confirmation
|
|
54
|
-
// timeout is the maximum time to wait for the transaction response
|
|
55
|
-
// delay is the polling interval, i.e. the time to wait between requests
|
|
56
|
-
// maxFails is the maximum number of hard failures to tolerate before giving up
|
|
57
|
-
wait(
|
|
58
|
-
metaTxnId: string | commons.transaction.SignedTransactionBundle,
|
|
59
|
-
timeout?: number,
|
|
60
|
-
delay?: number,
|
|
61
|
-
maxFails?: number
|
|
62
|
-
): Promise<commons.transaction.TransactionResponse>
|
|
63
|
-
|
|
64
|
-
// getMetaTransactions returns a list of meta transactions for a given project and gas tank
|
|
65
|
-
getMetaTransactions(
|
|
66
|
-
projectId: number,
|
|
67
|
-
page?: proto.Page
|
|
68
|
-
): Promise<{
|
|
69
|
-
page: proto.Page
|
|
70
|
-
transactions: proto.MetaTxnLog[]
|
|
71
|
-
}>
|
|
72
|
-
|
|
73
|
-
// getTransactionCost returns the used fee cost for gas tank during a given period
|
|
74
|
-
getTransactionCost(
|
|
75
|
-
projectId: number,
|
|
76
|
-
from: string,
|
|
77
|
-
to: string
|
|
78
|
-
): Promise<{
|
|
79
|
-
cost: number
|
|
80
|
-
}>
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export * from './local-relayer'
|
|
84
|
-
export * from './provider-relayer'
|
|
85
|
-
export * from './rpc-relayer'
|
|
86
|
-
export { proto as RpcRelayerProto } from './rpc-relayer'
|
|
87
|
-
export type SimulateResult = proto.SimulateResult
|
|
88
|
-
export type FeeOption = proto.FeeOption
|
|
89
|
-
|
|
90
|
-
// A fee quote is simply an opaque value that can be obtained via Relayer.getFeeOptions(), and
|
|
91
|
-
// returned back to the same relayer via Relayer.relay(). Fee quotes should be treated as an
|
|
92
|
-
// implementation detail of the relayer that produces them.
|
|
93
|
-
//
|
|
94
|
-
// This interface exists for type-safety purposes to protect against passing non-FeeQuotes to
|
|
95
|
-
// Relayer.relay(), or any other functions that call it indirectly (e.g. Account.sendTransaction).
|
|
96
|
-
export interface FeeQuote {
|
|
97
|
-
_tag: 'FeeQuote'
|
|
98
|
-
_quote: unknown
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function isRelayer(cand: any): cand is Relayer {
|
|
102
|
-
return (
|
|
103
|
-
typeof cand === 'object' &&
|
|
104
|
-
typeof cand.simulate === 'function' &&
|
|
105
|
-
typeof cand.getFeeOptions === 'function' &&
|
|
106
|
-
typeof cand.gasRefundOptions === 'function' &&
|
|
107
|
-
typeof cand.getNonce === 'function' &&
|
|
108
|
-
typeof cand.relay === 'function' &&
|
|
109
|
-
typeof cand.wait === 'function'
|
|
110
|
-
)
|
|
111
|
-
}
|
|
1
|
+
export * as Relayer from './relayer/index.js'
|
|
2
|
+
export * as RpcRelayerGen from './relayer/rpc-relayer/relayer.gen.js'
|
|
3
|
+
export * as Preconditions from './preconditions/index.js'
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { Address } from 'ox'
|
|
2
|
+
import {
|
|
3
|
+
Precondition,
|
|
4
|
+
NativeBalancePrecondition,
|
|
5
|
+
Erc20BalancePrecondition,
|
|
6
|
+
Erc20ApprovalPrecondition,
|
|
7
|
+
Erc721OwnershipPrecondition,
|
|
8
|
+
Erc721ApprovalPrecondition,
|
|
9
|
+
Erc1155BalancePrecondition,
|
|
10
|
+
Erc1155ApprovalPrecondition,
|
|
11
|
+
} from './types.js'
|
|
12
|
+
|
|
13
|
+
export interface TransactionPrecondition {
|
|
14
|
+
type: string
|
|
15
|
+
chainId: number
|
|
16
|
+
ownerAddress: string
|
|
17
|
+
tokenAddress: string
|
|
18
|
+
minAmount: bigint
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function decodePreconditions(preconditions: TransactionPrecondition[]): Precondition[] {
|
|
22
|
+
const decodedPreconditions: Precondition[] = []
|
|
23
|
+
|
|
24
|
+
for (const p of preconditions) {
|
|
25
|
+
const decoded = decodePrecondition(p)
|
|
26
|
+
if (decoded) {
|
|
27
|
+
decodedPreconditions.push(decoded)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return decodedPreconditions
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function decodePrecondition(p: TransactionPrecondition): Precondition | undefined {
|
|
35
|
+
if (!p) {
|
|
36
|
+
return undefined
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let precondition: Precondition | undefined
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
switch (p.type) {
|
|
43
|
+
case 'native-balance':
|
|
44
|
+
precondition = new NativeBalancePrecondition(Address.from(p.ownerAddress), p.minAmount, undefined)
|
|
45
|
+
break
|
|
46
|
+
|
|
47
|
+
case 'erc20-balance':
|
|
48
|
+
precondition = new Erc20BalancePrecondition(
|
|
49
|
+
Address.from(p.ownerAddress),
|
|
50
|
+
Address.from(p.tokenAddress),
|
|
51
|
+
p.minAmount,
|
|
52
|
+
undefined,
|
|
53
|
+
)
|
|
54
|
+
break
|
|
55
|
+
|
|
56
|
+
case 'erc20-approval':
|
|
57
|
+
precondition = new Erc20ApprovalPrecondition(
|
|
58
|
+
Address.from(p.ownerAddress),
|
|
59
|
+
Address.from(p.tokenAddress),
|
|
60
|
+
Address.from(p.ownerAddress),
|
|
61
|
+
p.minAmount,
|
|
62
|
+
)
|
|
63
|
+
break
|
|
64
|
+
|
|
65
|
+
case 'erc721-ownership':
|
|
66
|
+
precondition = new Erc721OwnershipPrecondition(
|
|
67
|
+
Address.from(p.ownerAddress),
|
|
68
|
+
Address.from(p.tokenAddress),
|
|
69
|
+
BigInt(0),
|
|
70
|
+
true,
|
|
71
|
+
)
|
|
72
|
+
break
|
|
73
|
+
|
|
74
|
+
case 'erc721-approval':
|
|
75
|
+
precondition = new Erc721ApprovalPrecondition(
|
|
76
|
+
Address.from(p.ownerAddress),
|
|
77
|
+
Address.from(p.tokenAddress),
|
|
78
|
+
BigInt(0),
|
|
79
|
+
Address.from(p.ownerAddress),
|
|
80
|
+
)
|
|
81
|
+
break
|
|
82
|
+
|
|
83
|
+
case 'erc1155-balance':
|
|
84
|
+
precondition = new Erc1155BalancePrecondition(
|
|
85
|
+
Address.from(p.ownerAddress),
|
|
86
|
+
Address.from(p.tokenAddress),
|
|
87
|
+
BigInt(0),
|
|
88
|
+
p.minAmount,
|
|
89
|
+
undefined,
|
|
90
|
+
)
|
|
91
|
+
break
|
|
92
|
+
|
|
93
|
+
case 'erc1155-approval':
|
|
94
|
+
precondition = new Erc1155ApprovalPrecondition(
|
|
95
|
+
Address.from(p.ownerAddress),
|
|
96
|
+
Address.from(p.tokenAddress),
|
|
97
|
+
BigInt(0),
|
|
98
|
+
Address.from(p.ownerAddress),
|
|
99
|
+
p.minAmount,
|
|
100
|
+
)
|
|
101
|
+
break
|
|
102
|
+
|
|
103
|
+
default:
|
|
104
|
+
return undefined
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const error = precondition.isValid()
|
|
108
|
+
if (error) {
|
|
109
|
+
console.warn(`Invalid precondition: ${error.message}`)
|
|
110
|
+
return undefined
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return precondition
|
|
114
|
+
} catch (e) {
|
|
115
|
+
console.warn(`Failed to decode precondition: ${e}`)
|
|
116
|
+
return undefined
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function encodePrecondition(p: Precondition): string {
|
|
121
|
+
const data: any = {}
|
|
122
|
+
|
|
123
|
+
switch (p.type()) {
|
|
124
|
+
case 'native-balance': {
|
|
125
|
+
const native = p as NativeBalancePrecondition
|
|
126
|
+
data.address = native.address.toString()
|
|
127
|
+
if (native.min !== undefined) data.min = native.min.toString()
|
|
128
|
+
if (native.max !== undefined) data.max = native.max.toString()
|
|
129
|
+
break
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
case 'erc20-balance': {
|
|
133
|
+
const erc20 = p as Erc20BalancePrecondition
|
|
134
|
+
data.address = erc20.address.toString()
|
|
135
|
+
data.token = erc20.token.toString()
|
|
136
|
+
if (erc20.min !== undefined) data.min = erc20.min.toString()
|
|
137
|
+
if (erc20.max !== undefined) data.max = erc20.max.toString()
|
|
138
|
+
break
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
case 'erc20-approval': {
|
|
142
|
+
const erc20 = p as Erc20ApprovalPrecondition
|
|
143
|
+
data.address = erc20.address.toString()
|
|
144
|
+
data.token = erc20.token.toString()
|
|
145
|
+
data.operator = erc20.operator.toString()
|
|
146
|
+
data.min = erc20.min.toString()
|
|
147
|
+
break
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
case 'erc721-ownership': {
|
|
151
|
+
const erc721 = p as Erc721OwnershipPrecondition
|
|
152
|
+
data.address = erc721.address.toString()
|
|
153
|
+
data.token = erc721.token.toString()
|
|
154
|
+
data.tokenId = erc721.tokenId.toString()
|
|
155
|
+
if (erc721.owned !== undefined) data.owned = erc721.owned
|
|
156
|
+
break
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
case 'erc721-approval': {
|
|
160
|
+
const erc721 = p as Erc721ApprovalPrecondition
|
|
161
|
+
data.address = erc721.address.toString()
|
|
162
|
+
data.token = erc721.token.toString()
|
|
163
|
+
data.tokenId = erc721.tokenId.toString()
|
|
164
|
+
data.operator = erc721.operator.toString()
|
|
165
|
+
break
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
case 'erc1155-balance': {
|
|
169
|
+
const erc1155 = p as Erc1155BalancePrecondition
|
|
170
|
+
data.address = erc1155.address.toString()
|
|
171
|
+
data.token = erc1155.token.toString()
|
|
172
|
+
data.tokenId = erc1155.tokenId.toString()
|
|
173
|
+
if (erc1155.min !== undefined) data.min = erc1155.min.toString()
|
|
174
|
+
if (erc1155.max !== undefined) data.max = erc1155.max.toString()
|
|
175
|
+
break
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
case 'erc1155-approval': {
|
|
179
|
+
const erc1155 = p as Erc1155ApprovalPrecondition
|
|
180
|
+
data.address = erc1155.address.toString()
|
|
181
|
+
data.token = erc1155.token.toString()
|
|
182
|
+
data.tokenId = erc1155.tokenId.toString()
|
|
183
|
+
data.operator = erc1155.operator.toString()
|
|
184
|
+
data.min = erc1155.min.toString()
|
|
185
|
+
break
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return JSON.stringify(data)
|
|
190
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Precondition, NativeBalancePrecondition, Erc20BalancePrecondition } from './types.js'
|
|
2
|
+
import { TransactionPrecondition, decodePreconditions } from './codec.js'
|
|
3
|
+
|
|
4
|
+
export function extractChainID(precondition: TransactionPrecondition): number | undefined {
|
|
5
|
+
if (!precondition) {
|
|
6
|
+
return undefined
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return precondition.chainId
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function extractSupportedPreconditions(preconditions: TransactionPrecondition[]): Precondition[] {
|
|
13
|
+
if (!preconditions || preconditions.length === 0) {
|
|
14
|
+
return []
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return decodePreconditions(preconditions)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function extractNativeBalancePreconditions(
|
|
21
|
+
preconditions: TransactionPrecondition[],
|
|
22
|
+
): NativeBalancePrecondition[] {
|
|
23
|
+
if (!preconditions || preconditions.length === 0) {
|
|
24
|
+
return []
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const decoded = decodePreconditions(preconditions)
|
|
28
|
+
return decoded.filter((p): p is NativeBalancePrecondition => p.type() === 'native-balance')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function extractERC20BalancePreconditions(preconditions: TransactionPrecondition[]): Erc20BalancePrecondition[] {
|
|
32
|
+
if (!preconditions || preconditions.length === 0) {
|
|
33
|
+
return []
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const decoded = decodePreconditions(preconditions)
|
|
37
|
+
return decoded.filter((p): p is Erc20BalancePrecondition => p.type() === 'erc20-balance')
|
|
38
|
+
}
|