ox 1.0.2 → 1.0.4
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/CHANGELOG.md +12 -0
- package/dist/tempo/EarnShares.d.ts +179 -0
- package/dist/tempo/EarnShares.d.ts.map +1 -0
- package/dist/tempo/EarnShares.js +160 -0
- package/dist/tempo/EarnShares.js.map +1 -0
- package/dist/tempo/KeyAuthorization.d.ts +22 -16
- package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/KeyAuthorization.js +31 -7
- package/dist/tempo/KeyAuthorization.js.map +1 -1
- package/dist/tempo/MultisigConfig.d.ts +8 -4
- package/dist/tempo/MultisigConfig.d.ts.map +1 -1
- package/dist/tempo/MultisigConfig.js +16 -4
- package/dist/tempo/MultisigConfig.js.map +1 -1
- package/dist/tempo/SignatureEnvelope.d.ts +31 -13
- package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.js +172 -55
- package/dist/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/tempo/index.d.ts +22 -0
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +22 -0
- package/dist/tempo/index.js.map +1 -1
- package/dist/zod/tempo/AuthorizationTempo.d.ts +70 -60
- package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
- package/dist/zod/tempo/KeyAuthorization.d.ts +0 -306
- package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/zod/tempo/KeyAuthorization.js +2 -2
- package/dist/zod/tempo/KeyAuthorization.js.map +1 -1
- package/dist/zod/tempo/MultisigConfig.d.ts.map +1 -1
- package/dist/zod/tempo/MultisigConfig.js +5 -2
- package/dist/zod/tempo/MultisigConfig.js.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +28 -132
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.d.ts +79 -18
- package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.js +39 -11
- package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +84 -396
- package/dist/zod/tempo/Transaction.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.d.ts +42 -234
- package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts +0 -108
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/tempo/EarnShares.test.ts +120 -0
- package/src/tempo/EarnShares.ts +235 -0
- package/src/tempo/KeyAuthorization.test-d.ts +62 -0
- package/src/tempo/KeyAuthorization.test.ts +53 -0
- package/src/tempo/KeyAuthorization.ts +69 -27
- package/src/tempo/MultisigConfig.test.ts +34 -2
- package/src/tempo/MultisigConfig.ts +18 -4
- package/src/tempo/SignatureEnvelope.test-d.ts +97 -0
- package/src/tempo/SignatureEnvelope.test.ts +247 -36
- package/src/tempo/SignatureEnvelope.ts +271 -85
- package/src/tempo/e2e.test.ts +0 -416
- package/src/tempo/index.ts +22 -0
- package/src/tempo/multisig.e2e.test.ts +582 -0
- package/src/version.ts +1 -1
- package/src/zod/tempo/KeyAuthorization.ts +2 -2
- package/src/zod/tempo/MultisigConfig.ts +13 -5
- package/src/zod/tempo/SignatureEnvelope.ts +81 -18
- package/src/zod/tempo/_test/KeyAuthorization.test.ts +25 -0
- package/src/zod/tempo/_test/MultisigConfig.test.ts +44 -0
- package/src/zod/tempo/_test/SignatureEnvelope.test-d.ts +9 -0
- package/src/zod/tempo/_test/SignatureEnvelope.test.ts +142 -6
package/src/tempo/e2e.test.ts
CHANGED
|
@@ -14,7 +14,6 @@ import { chain, client, fundAddress, nodeEnv } from '../../test/tempo/config.js'
|
|
|
14
14
|
import {
|
|
15
15
|
AuthorizationTempo,
|
|
16
16
|
KeyAuthorization,
|
|
17
|
-
MultisigConfig,
|
|
18
17
|
Period,
|
|
19
18
|
SignatureEnvelope,
|
|
20
19
|
} from './index.js'
|
|
@@ -2968,418 +2967,3 @@ describe('behavior: keyAuthorization', () => {
|
|
|
2968
2967
|
},
|
|
2969
2968
|
)
|
|
2970
2969
|
})
|
|
2971
|
-
|
|
2972
|
-
// TODO: unskip once TIP-1061 native multisig is deployed to the standard
|
|
2973
|
-
// localnet/testnet nodes. Until then these only pass against the dedicated
|
|
2974
|
-
// PR-5178 devnet (run with VITE_TEMPO_RPC_URL pointed at it).
|
|
2975
|
-
describe.skip('behavior: multisig (TIP-1061)', () => {
|
|
2976
|
-
// Helper: builds a fresh set of secp256k1 owners + the derived config.
|
|
2977
|
-
function setup(parameters: { count: number; threshold: number }) {
|
|
2978
|
-
const { count, threshold } = parameters
|
|
2979
|
-
const ownerKeys = Array.from({ length: count }, () => {
|
|
2980
|
-
const privateKey = Secp256k1.randomPrivateKey()
|
|
2981
|
-
const address = Address.fromPublicKey(
|
|
2982
|
-
Secp256k1.getPublicKey({ privateKey }),
|
|
2983
|
-
)
|
|
2984
|
-
return { address, privateKey } as const
|
|
2985
|
-
})
|
|
2986
|
-
|
|
2987
|
-
const genesisConfig = MultisigConfig.from({
|
|
2988
|
-
// A fresh random salt yields a distinct account each run, exercising the
|
|
2989
|
-
// salt-inclusive account derivation against the node.
|
|
2990
|
-
salt: Hex.random(32),
|
|
2991
|
-
threshold,
|
|
2992
|
-
owners: ownerKeys.map((key) => ({
|
|
2993
|
-
owner: key.address,
|
|
2994
|
-
weight: 1,
|
|
2995
|
-
})),
|
|
2996
|
-
})
|
|
2997
|
-
const account = MultisigConfig.getAddress(genesisConfig)
|
|
2998
|
-
|
|
2999
|
-
return { account, genesisConfig, ownerKeys } as const
|
|
3000
|
-
}
|
|
3001
|
-
|
|
3002
|
-
// Signs the multisig owner digest with the provided owner keys, returning
|
|
3003
|
-
// primitive approval envelopes ordered strictly ascending by recovered owner
|
|
3004
|
-
// address (required by the node: "recovered owners must be strictly
|
|
3005
|
-
// ascending").
|
|
3006
|
-
function approve(parameters: {
|
|
3007
|
-
genesisConfig: MultisigConfig.Config
|
|
3008
|
-
payload: Hex.Hex
|
|
3009
|
-
signers: readonly { privateKey: Hex.Hex }[]
|
|
3010
|
-
}) {
|
|
3011
|
-
const { genesisConfig, payload, signers } = parameters
|
|
3012
|
-
const digest = MultisigConfig.getSignPayload({ payload, genesisConfig })
|
|
3013
|
-
const signatures = signers.map((signer) =>
|
|
3014
|
-
SignatureEnvelope.from(
|
|
3015
|
-
Secp256k1.sign({ payload: digest, privateKey: signer.privateKey }),
|
|
3016
|
-
),
|
|
3017
|
-
)
|
|
3018
|
-
return SignatureEnvelope.sortMultisigApprovals({
|
|
3019
|
-
genesisConfig,
|
|
3020
|
-
payload,
|
|
3021
|
-
signatures,
|
|
3022
|
-
})
|
|
3023
|
-
}
|
|
3024
|
-
|
|
3025
|
-
test('behavior: bootstrap + spend (2-of-3 secp256k1)', async () => {
|
|
3026
|
-
const { account, genesisConfig, ownerKeys } = setup({
|
|
3027
|
-
count: 3,
|
|
3028
|
-
threshold: 2,
|
|
3029
|
-
})
|
|
3030
|
-
|
|
3031
|
-
// The derived multisig account pays its own fees.
|
|
3032
|
-
await fundAddress(client, { address: account })
|
|
3033
|
-
|
|
3034
|
-
// Bootstrap (first transaction): the bootstrap config travels in the
|
|
3035
|
-
// multisig signature `init`, nonce 0.
|
|
3036
|
-
const bootstrap = TxEnvelopeTempo.from({
|
|
3037
|
-
calls: [{ to: '0x0000000000000000000000000000000000000000' }],
|
|
3038
|
-
chainId,
|
|
3039
|
-
feeToken: '0x20c0000000000000000000000000000000000001',
|
|
3040
|
-
nonce: 0n,
|
|
3041
|
-
gas: 2_000_000n,
|
|
3042
|
-
maxFeePerGas: Value.fromGwei('20'),
|
|
3043
|
-
maxPriorityFeePerGas: Value.fromGwei('10'),
|
|
3044
|
-
})
|
|
3045
|
-
|
|
3046
|
-
const bootstrap_signed = TxEnvelopeTempo.serialize(bootstrap, {
|
|
3047
|
-
signature: SignatureEnvelope.from({
|
|
3048
|
-
genesisConfig,
|
|
3049
|
-
// Initialize multisig.
|
|
3050
|
-
init: true,
|
|
3051
|
-
// Approve with 2 of the 3 owners to satisfy the threshold.
|
|
3052
|
-
signatures: approve({
|
|
3053
|
-
genesisConfig,
|
|
3054
|
-
payload: TxEnvelopeTempo.getSignPayload(bootstrap),
|
|
3055
|
-
signers: [ownerKeys[0]!, ownerKeys[1]!],
|
|
3056
|
-
}),
|
|
3057
|
-
}),
|
|
3058
|
-
})
|
|
3059
|
-
|
|
3060
|
-
const bootstrap_receipt = (await client
|
|
3061
|
-
.request({
|
|
3062
|
-
method: 'eth_sendRawTransactionSync',
|
|
3063
|
-
params: [bootstrap_signed],
|
|
3064
|
-
})
|
|
3065
|
-
.then((tx) => TransactionReceipt.fromRpc(tx as any)))!
|
|
3066
|
-
expect(bootstrap_receipt).toBeDefined()
|
|
3067
|
-
expect(bootstrap_receipt.status).toBe('success')
|
|
3068
|
-
expect(bootstrap_receipt.from).toBe(account)
|
|
3069
|
-
|
|
3070
|
-
{
|
|
3071
|
-
const response = await client
|
|
3072
|
-
.request({
|
|
3073
|
-
method: 'eth_getTransactionByHash',
|
|
3074
|
-
params: [bootstrap_receipt.transactionHash],
|
|
3075
|
-
})
|
|
3076
|
-
.then((tx) => Transaction.fromRpc(tx as any))
|
|
3077
|
-
if (!response) throw new Error()
|
|
3078
|
-
expect(response.from).toBe(account)
|
|
3079
|
-
expect(response.signature?.type).toBe('multisig')
|
|
3080
|
-
// The bootstrap config is carried by the multisig signature `init`.
|
|
3081
|
-
expect(
|
|
3082
|
-
(response.signature as SignatureEnvelope.Multisig | undefined)?.init,
|
|
3083
|
-
).toEqual(genesisConfig)
|
|
3084
|
-
}
|
|
3085
|
-
|
|
3086
|
-
// Spend (subsequent transaction): no signature `init`, nonce 1, uses the
|
|
3087
|
-
// stored config loaded by the node.
|
|
3088
|
-
const nonce = await getTransactionCount(client, {
|
|
3089
|
-
address: account,
|
|
3090
|
-
blockTag: 'pending',
|
|
3091
|
-
})
|
|
3092
|
-
|
|
3093
|
-
const spend = TxEnvelopeTempo.from({
|
|
3094
|
-
calls: [{ to: '0x0000000000000000000000000000000000000000' }],
|
|
3095
|
-
chainId,
|
|
3096
|
-
feeToken: '0x20c0000000000000000000000000000000000001',
|
|
3097
|
-
nonce: BigInt(nonce),
|
|
3098
|
-
gas: 2_000_000n,
|
|
3099
|
-
maxFeePerGas: Value.fromGwei('20'),
|
|
3100
|
-
maxPriorityFeePerGas: Value.fromGwei('10'),
|
|
3101
|
-
})
|
|
3102
|
-
|
|
3103
|
-
const spend_signed = TxEnvelopeTempo.serialize(spend, {
|
|
3104
|
-
signature: SignatureEnvelope.from({
|
|
3105
|
-
genesisConfig,
|
|
3106
|
-
// A different 2-of-3 subset still authorizes the transaction.
|
|
3107
|
-
signatures: approve({
|
|
3108
|
-
genesisConfig,
|
|
3109
|
-
payload: TxEnvelopeTempo.getSignPayload(spend),
|
|
3110
|
-
signers: [ownerKeys[1]!, ownerKeys[2]!],
|
|
3111
|
-
}),
|
|
3112
|
-
}),
|
|
3113
|
-
})
|
|
3114
|
-
|
|
3115
|
-
const spend_receipt = (await client
|
|
3116
|
-
.request({
|
|
3117
|
-
method: 'eth_sendRawTransactionSync',
|
|
3118
|
-
params: [spend_signed],
|
|
3119
|
-
})
|
|
3120
|
-
.then((tx) => TransactionReceipt.fromRpc(tx as any)))!
|
|
3121
|
-
expect(spend_receipt).toBeDefined()
|
|
3122
|
-
expect(spend_receipt.status).toBe('success')
|
|
3123
|
-
expect(spend_receipt.from).toBe(account)
|
|
3124
|
-
})
|
|
3125
|
-
|
|
3126
|
-
test('behavior: rejects below-threshold approvals', async () => {
|
|
3127
|
-
const { account, genesisConfig, ownerKeys } = setup({
|
|
3128
|
-
count: 3,
|
|
3129
|
-
threshold: 2,
|
|
3130
|
-
})
|
|
3131
|
-
|
|
3132
|
-
await fundAddress(client, { address: account })
|
|
3133
|
-
|
|
3134
|
-
const bootstrap = TxEnvelopeTempo.from({
|
|
3135
|
-
calls: [{ to: '0x0000000000000000000000000000000000000000' }],
|
|
3136
|
-
chainId,
|
|
3137
|
-
feeToken: '0x20c0000000000000000000000000000000000001',
|
|
3138
|
-
nonce: 0n,
|
|
3139
|
-
gas: 2_000_000n,
|
|
3140
|
-
maxFeePerGas: Value.fromGwei('20'),
|
|
3141
|
-
maxPriorityFeePerGas: Value.fromGwei('10'),
|
|
3142
|
-
})
|
|
3143
|
-
|
|
3144
|
-
const serialized_signed = TxEnvelopeTempo.serialize(bootstrap, {
|
|
3145
|
-
signature: SignatureEnvelope.from({
|
|
3146
|
-
genesisConfig,
|
|
3147
|
-
// Opt into bootstrap: writes `genesisConfig` into the signature `init`.
|
|
3148
|
-
init: true,
|
|
3149
|
-
// Only one approval — below the threshold of 2.
|
|
3150
|
-
signatures: approve({
|
|
3151
|
-
genesisConfig,
|
|
3152
|
-
payload: TxEnvelopeTempo.getSignPayload(bootstrap),
|
|
3153
|
-
signers: [ownerKeys[0]!],
|
|
3154
|
-
}),
|
|
3155
|
-
}),
|
|
3156
|
-
})
|
|
3157
|
-
|
|
3158
|
-
await expect(
|
|
3159
|
-
client.request({
|
|
3160
|
-
method: 'eth_sendRawTransactionSync',
|
|
3161
|
-
params: [serialized_signed],
|
|
3162
|
-
}),
|
|
3163
|
-
).rejects.toThrow()
|
|
3164
|
-
})
|
|
3165
|
-
|
|
3166
|
-
test('behavior: keychain access key (authorize via AccountKeychain + spend)', async () => {
|
|
3167
|
-
const { account, genesisConfig, ownerKeys } = setup({
|
|
3168
|
-
count: 2,
|
|
3169
|
-
threshold: 2,
|
|
3170
|
-
})
|
|
3171
|
-
|
|
3172
|
-
await fundAddress(client, { address: account })
|
|
3173
|
-
|
|
3174
|
-
// Fresh secp256k1 access key for the multisig account.
|
|
3175
|
-
const access = (() => {
|
|
3176
|
-
const privateKey = Secp256k1.randomPrivateKey()
|
|
3177
|
-
const address = Address.fromPublicKey(
|
|
3178
|
-
Secp256k1.getPublicKey({ privateKey }),
|
|
3179
|
-
)
|
|
3180
|
-
return { address, privateKey } as const
|
|
3181
|
-
})()
|
|
3182
|
-
|
|
3183
|
-
// Bootstrap the multisig (nonce 0).
|
|
3184
|
-
const bootstrap = TxEnvelopeTempo.from({
|
|
3185
|
-
calls: [{ to: '0x0000000000000000000000000000000000000000' }],
|
|
3186
|
-
chainId,
|
|
3187
|
-
feeToken: '0x20c0000000000000000000000000000000000001',
|
|
3188
|
-
nonce: 0n,
|
|
3189
|
-
gas: 2_000_000n,
|
|
3190
|
-
maxFeePerGas: Value.fromGwei('20'),
|
|
3191
|
-
maxPriorityFeePerGas: Value.fromGwei('10'),
|
|
3192
|
-
})
|
|
3193
|
-
|
|
3194
|
-
const bootstrap_signed = TxEnvelopeTempo.serialize(bootstrap, {
|
|
3195
|
-
signature: SignatureEnvelope.from({
|
|
3196
|
-
genesisConfig,
|
|
3197
|
-
init: true,
|
|
3198
|
-
signatures: approve({
|
|
3199
|
-
genesisConfig,
|
|
3200
|
-
payload: TxEnvelopeTempo.getSignPayload(bootstrap),
|
|
3201
|
-
signers: ownerKeys,
|
|
3202
|
-
}),
|
|
3203
|
-
}),
|
|
3204
|
-
})
|
|
3205
|
-
|
|
3206
|
-
const bootstrap_receipt = (await client
|
|
3207
|
-
.request({
|
|
3208
|
-
method: 'eth_sendRawTransactionSync',
|
|
3209
|
-
params: [bootstrap_signed],
|
|
3210
|
-
})
|
|
3211
|
-
.then((tx) => TransactionReceipt.fromRpc(tx as any)))!
|
|
3212
|
-
expect(bootstrap_receipt.status).toBe('success')
|
|
3213
|
-
|
|
3214
|
-
// Authorize the access key (nonce 1): multisig transactions cannot carry
|
|
3215
|
-
// a TIP-1049 `keyAuthorization`, so the multisig account calls the
|
|
3216
|
-
// AccountKeychain precompile's `authorizeKey` directly.
|
|
3217
|
-
const authorizeKey = AbiFunction.from(
|
|
3218
|
-
'function authorizeKey(address keyId, uint8 signatureType, (uint64 expiry, bool enforceLimits, (address token, uint256 amount, uint64 period)[] limits, bool allowAnyCalls, (address target, (bytes4 selector, address[] recipients)[] selectorRules)[] allowedCalls) config)',
|
|
3219
|
-
)
|
|
3220
|
-
|
|
3221
|
-
const authorize = TxEnvelopeTempo.from({
|
|
3222
|
-
calls: [
|
|
3223
|
-
{
|
|
3224
|
-
to: '0xaaaaaaaa00000000000000000000000000000000',
|
|
3225
|
-
data: AbiFunction.encodeData(authorizeKey, [
|
|
3226
|
-
access.address,
|
|
3227
|
-
0, // secp256k1
|
|
3228
|
-
{
|
|
3229
|
-
expiry: 18446744073709551615n, // u64 max (never expires)
|
|
3230
|
-
enforceLimits: false,
|
|
3231
|
-
limits: [],
|
|
3232
|
-
allowAnyCalls: true,
|
|
3233
|
-
allowedCalls: [],
|
|
3234
|
-
},
|
|
3235
|
-
]),
|
|
3236
|
-
},
|
|
3237
|
-
],
|
|
3238
|
-
chainId,
|
|
3239
|
-
feeToken: '0x20c0000000000000000000000000000000000001',
|
|
3240
|
-
nonce: 1n,
|
|
3241
|
-
gas: 2_000_000n,
|
|
3242
|
-
maxFeePerGas: Value.fromGwei('20'),
|
|
3243
|
-
maxPriorityFeePerGas: Value.fromGwei('10'),
|
|
3244
|
-
})
|
|
3245
|
-
|
|
3246
|
-
const authorize_signed = TxEnvelopeTempo.serialize(authorize, {
|
|
3247
|
-
signature: SignatureEnvelope.from({
|
|
3248
|
-
genesisConfig,
|
|
3249
|
-
signatures: approve({
|
|
3250
|
-
genesisConfig,
|
|
3251
|
-
payload: TxEnvelopeTempo.getSignPayload(authorize),
|
|
3252
|
-
signers: ownerKeys,
|
|
3253
|
-
}),
|
|
3254
|
-
}),
|
|
3255
|
-
})
|
|
3256
|
-
|
|
3257
|
-
const authorize_receipt = (await client
|
|
3258
|
-
.request({
|
|
3259
|
-
method: 'eth_sendRawTransactionSync',
|
|
3260
|
-
params: [authorize_signed],
|
|
3261
|
-
})
|
|
3262
|
-
.then((tx) => TransactionReceipt.fromRpc(tx as any)))!
|
|
3263
|
-
expect(authorize_receipt.status).toBe('success')
|
|
3264
|
-
expect(authorize_receipt.from).toBe(account)
|
|
3265
|
-
|
|
3266
|
-
// Spend with the access key (nonce 2): outer keychain signature with the
|
|
3267
|
-
// multisig account as `userAddress` — no owner approvals involved.
|
|
3268
|
-
const nonce = await getTransactionCount(client, {
|
|
3269
|
-
address: account,
|
|
3270
|
-
blockTag: 'pending',
|
|
3271
|
-
})
|
|
3272
|
-
|
|
3273
|
-
const spend = TxEnvelopeTempo.from({
|
|
3274
|
-
calls: [{ to: '0x0000000000000000000000000000000000000000' }],
|
|
3275
|
-
chainId,
|
|
3276
|
-
feeToken: '0x20c0000000000000000000000000000000000001',
|
|
3277
|
-
nonce: BigInt(nonce),
|
|
3278
|
-
gas: 2_000_000n,
|
|
3279
|
-
maxFeePerGas: Value.fromGwei('20'),
|
|
3280
|
-
maxPriorityFeePerGas: Value.fromGwei('10'),
|
|
3281
|
-
})
|
|
3282
|
-
|
|
3283
|
-
const spend_signature = Secp256k1.sign({
|
|
3284
|
-
payload: TxEnvelopeTempo.getSignPayload(spend, { from: account }),
|
|
3285
|
-
privateKey: access.privateKey,
|
|
3286
|
-
})
|
|
3287
|
-
|
|
3288
|
-
const spend_signed = TxEnvelopeTempo.serialize(spend, {
|
|
3289
|
-
signature: SignatureEnvelope.from({
|
|
3290
|
-
userAddress: account,
|
|
3291
|
-
inner: SignatureEnvelope.from(spend_signature),
|
|
3292
|
-
type: 'keychain',
|
|
3293
|
-
}),
|
|
3294
|
-
})
|
|
3295
|
-
|
|
3296
|
-
const spend_receipt = (await client
|
|
3297
|
-
.request({
|
|
3298
|
-
method: 'eth_sendRawTransactionSync',
|
|
3299
|
-
params: [spend_signed],
|
|
3300
|
-
})
|
|
3301
|
-
.then((tx) => TransactionReceipt.fromRpc(tx as any)))!
|
|
3302
|
-
expect(spend_receipt.status).toBe('success')
|
|
3303
|
-
expect(spend_receipt.from).toBe(account)
|
|
3304
|
-
|
|
3305
|
-
{
|
|
3306
|
-
const response = await client
|
|
3307
|
-
.request({
|
|
3308
|
-
method: 'eth_getTransactionByHash',
|
|
3309
|
-
params: [spend_receipt.transactionHash],
|
|
3310
|
-
})
|
|
3311
|
-
.then((tx) => Transaction.fromRpc(tx as any))
|
|
3312
|
-
if (!response) throw new Error()
|
|
3313
|
-
expect(response.from).toBe(account)
|
|
3314
|
-
expect(response.signature?.type).toBe('keychain')
|
|
3315
|
-
expect(
|
|
3316
|
-
(response.signature as SignatureEnvelope.Keychain | undefined)
|
|
3317
|
-
?.userAddress,
|
|
3318
|
-
).toBe(account)
|
|
3319
|
-
}
|
|
3320
|
-
})
|
|
3321
|
-
|
|
3322
|
-
test('behavior: rejects `keyAuthorization` on multisig transactions', async () => {
|
|
3323
|
-
const { account, genesisConfig, ownerKeys } = setup({
|
|
3324
|
-
count: 1,
|
|
3325
|
-
threshold: 1,
|
|
3326
|
-
})
|
|
3327
|
-
|
|
3328
|
-
await fundAddress(client, { address: account })
|
|
3329
|
-
|
|
3330
|
-
const access = (() => {
|
|
3331
|
-
const privateKey = Secp256k1.randomPrivateKey()
|
|
3332
|
-
const address = Address.fromPublicKey(
|
|
3333
|
-
Secp256k1.getPublicKey({ privateKey }),
|
|
3334
|
-
)
|
|
3335
|
-
return { address, privateKey } as const
|
|
3336
|
-
})()
|
|
3337
|
-
|
|
3338
|
-
// TIP-1049 key authorizations are only valid on primitive/keychain
|
|
3339
|
-
// transactions; multisig accounts must call the AccountKeychain
|
|
3340
|
-
// precompile instead.
|
|
3341
|
-
const keyAuth = KeyAuthorization.from({
|
|
3342
|
-
address: access.address,
|
|
3343
|
-
chainId: BigInt(chainId),
|
|
3344
|
-
type: 'secp256k1',
|
|
3345
|
-
})
|
|
3346
|
-
const keyAuth_signed = KeyAuthorization.from(keyAuth, {
|
|
3347
|
-
signature: SignatureEnvelope.from(
|
|
3348
|
-
Secp256k1.sign({
|
|
3349
|
-
payload: KeyAuthorization.getSignPayload(keyAuth),
|
|
3350
|
-
privateKey: ownerKeys[0]!.privateKey,
|
|
3351
|
-
}),
|
|
3352
|
-
),
|
|
3353
|
-
})
|
|
3354
|
-
|
|
3355
|
-
const bootstrap = TxEnvelopeTempo.from({
|
|
3356
|
-
calls: [{ to: '0x0000000000000000000000000000000000000000' }],
|
|
3357
|
-
chainId,
|
|
3358
|
-
feeToken: '0x20c0000000000000000000000000000000000001',
|
|
3359
|
-
keyAuthorization: keyAuth_signed,
|
|
3360
|
-
nonce: 0n,
|
|
3361
|
-
gas: 2_000_000n,
|
|
3362
|
-
maxFeePerGas: Value.fromGwei('20'),
|
|
3363
|
-
maxPriorityFeePerGas: Value.fromGwei('10'),
|
|
3364
|
-
})
|
|
3365
|
-
|
|
3366
|
-
const serialized_signed = TxEnvelopeTempo.serialize(bootstrap, {
|
|
3367
|
-
signature: SignatureEnvelope.from({
|
|
3368
|
-
genesisConfig,
|
|
3369
|
-
init: true,
|
|
3370
|
-
signatures: approve({
|
|
3371
|
-
genesisConfig,
|
|
3372
|
-
payload: TxEnvelopeTempo.getSignPayload(bootstrap),
|
|
3373
|
-
signers: ownerKeys,
|
|
3374
|
-
}),
|
|
3375
|
-
}),
|
|
3376
|
-
})
|
|
3377
|
-
|
|
3378
|
-
await expect(
|
|
3379
|
-
client.request({
|
|
3380
|
-
method: 'eth_sendRawTransactionSync',
|
|
3381
|
-
params: [serialized_signed],
|
|
3382
|
-
}),
|
|
3383
|
-
).rejects.toThrow()
|
|
3384
|
-
})
|
|
3385
|
-
})
|
package/src/tempo/index.ts
CHANGED
|
@@ -68,6 +68,28 @@ export * as AuthorizationTempo from './AuthorizationTempo.js'
|
|
|
68
68
|
* @category Reference
|
|
69
69
|
*/
|
|
70
70
|
export * as Channel from './Channel.js'
|
|
71
|
+
/**
|
|
72
|
+
* Tempo Earn `VaultAdapter` share math: raw vault-share and venue-share conversions
|
|
73
|
+
* at the anchor rate, the dilution-correct fee-share formula, and the
|
|
74
|
+
* `minimumOutput` slippage floor.
|
|
75
|
+
*
|
|
76
|
+
* Conversions are fee-blind mirrors of the adapter's anchor arithmetic; use the
|
|
77
|
+
* adapter's `previewRedeem` for user-facing value.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts twoslash
|
|
81
|
+
* import { EarnShares } from 'ox/tempo'
|
|
82
|
+
*
|
|
83
|
+
* const shareAmount = EarnShares.toAmount(
|
|
84
|
+
* { engineShares: 3n, shareSupply: 2n },
|
|
85
|
+
* 7n
|
|
86
|
+
* )
|
|
87
|
+
* // @log: 4n
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @category Reference
|
|
91
|
+
*/
|
|
92
|
+
export * as EarnShares from './EarnShares.js'
|
|
71
93
|
/**
|
|
72
94
|
* Tempo key authorization utilities for provisioning and signing access keys.
|
|
73
95
|
*
|