ox
Version:
Ethereum Standard Library
651 lines (621 loc) • 19.3 kB
text/typescript
import { Authorization, type Hex, Secp256k1 } from 'ox'
import { describe, expect, expectTypeOf, test } from 'vp/test'
import { accounts } from '../../../test/constants/accounts.js'
describe('from', () => {
test('default', () => {
{
const authorization = Authorization.from({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 1,
nonce: 40n,
})
expectTypeOf(authorization).toEqualTypeOf<{
readonly address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c'
readonly chainId: 1
readonly nonce: 40n
}>()
expectTypeOf(authorization).toMatchTypeOf<
Authorization.Authorization<false>
>()
expect(authorization).toMatchInlineSnapshot(
`
{
"address": "0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"chainId": 1,
"nonce": 40n,
}
`,
)
}
{
const authorization = Authorization.from({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 1,
nonce: 40n,
r: '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf',
s: '0x4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8',
yParity: 0,
})
expectTypeOf(authorization).toEqualTypeOf<{
readonly address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c'
readonly chainId: 1
readonly nonce: 40n
readonly r: '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf'
readonly s: '0x4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8'
readonly yParity: 0
}>()
expectTypeOf(authorization).toMatchTypeOf<
Authorization.Authorization<true>
>()
expect(authorization).toMatchInlineSnapshot(
`
{
"address": "0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"chainId": 1,
"nonce": 40n,
"r": "0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf",
"s": "0x4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8",
"yParity": 0,
}
`,
)
}
})
test('options: signature', () => {
const authorization = Authorization.from({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 1,
nonce: 40n,
})
const signature = Secp256k1.sign({
payload: Authorization.getSignPayload(authorization),
privateKey: accounts[0].privateKey,
})
const authorization_signed = Authorization.from(authorization, {
signature,
})
expectTypeOf(authorization_signed).toEqualTypeOf<{
readonly address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c'
readonly chainId: 1
readonly nonce: 40n
readonly r: Hex.Hex
readonly s: Hex.Hex
readonly yParity: number
}>()
expectTypeOf(authorization_signed).toMatchTypeOf<
Authorization.Authorization<true>
>()
expect(authorization_signed).toMatchInlineSnapshot(
`
{
"address": "0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"chainId": 1,
"nonce": 40n,
"r": "0xa513a287a156a99f2be4023466d401589e33add75c057e5fd376d11ed8946cf3",
"s": "0x371440c5f5c845ae5260bffccf9e28307b9204216faeaa0e27a6608bcdd7e047",
"yParity": 1,
}
`,
)
})
test('behavior: rpc', () => {
{
const authorization = Authorization.from({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: '0x1',
nonce: '0x1',
r: '0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d',
s: '0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540',
yParity: '0x0',
})
expectTypeOf(authorization).toMatchTypeOf<Authorization.Authorization>()
expect(authorization).toMatchInlineSnapshot(
`
{
"address": "0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"chainId": 1,
"nonce": 1n,
"r": "0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d",
"s": "0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540",
"yParity": 0,
}
`,
)
}
})
})
describe('fromRpc', () => {
test('default', () => {
expect(
Authorization.fromRpc({
address: '0x0000000000000000000000000000000000000000',
chainId: '0x1',
nonce: '0x1',
r: '0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d',
s: '0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540',
yParity: '0x0',
}),
).toMatchInlineSnapshot(`
{
"address": "0x0000000000000000000000000000000000000000",
"chainId": 1,
"nonce": 1n,
"r": "0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d",
"s": "0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540",
"yParity": 0,
}
`)
})
})
describe('fromRpcList', () => {
test('default', () => {
expect(
Authorization.fromRpcList([
{
address: '0x0000000000000000000000000000000000000000',
chainId: '0x1',
nonce: '0x1',
r: '0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d',
s: '0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540',
yParity: '0x0',
},
{
address: '0x0000000000000000000000000000000000000000',
chainId: '0x1',
nonce: '0x1',
r: '0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d',
s: '0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540',
yParity: '0x0',
},
]),
).toMatchInlineSnapshot(`
[
{
"address": "0x0000000000000000000000000000000000000000",
"chainId": 1,
"nonce": 1n,
"r": "0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d",
"s": "0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540",
"yParity": 0,
},
{
"address": "0x0000000000000000000000000000000000000000",
"chainId": 1,
"nonce": 1n,
"r": "0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d",
"s": "0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540",
"yParity": 0,
},
]
`)
})
})
describe('fromTuple', () => {
test('default', () => {
const tuple = [
'0x1',
'0x0000000000000000000000000000000000000000',
'0x3',
] as const satisfies Authorization.Tuple
const authorization = Authorization.fromTuple(tuple)
expect(authorization).toMatchInlineSnapshot(`
{
"address": "0x0000000000000000000000000000000000000000",
"chainId": 1,
"nonce": 3n,
}
`)
})
test('behavior: zeroish nonce + chainId', () => {
const tuple = [
'0x',
'0x0000000000000000000000000000000000000000',
'0x',
] as const satisfies Authorization.Tuple
const authorization = Authorization.fromTuple(tuple)
expect(authorization).toMatchInlineSnapshot(`
{
"address": "0x0000000000000000000000000000000000000000",
"chainId": 0,
"nonce": 0n,
}
`)
})
test('behavior: signature', () => {
const tuple = [
'0x1',
'0x0000000000000000000000000000000000000000',
'0x3',
'0x',
'0x01',
'0x02',
] as const satisfies Authorization.Tuple
const authorization = Authorization.fromTuple(tuple)
expect(authorization).toMatchInlineSnapshot(`
{
"address": "0x0000000000000000000000000000000000000000",
"chainId": 1,
"nonce": 3n,
"r": "0x0000000000000000000000000000000000000000000000000000000000000001",
"s": "0x0000000000000000000000000000000000000000000000000000000000000002",
"yParity": 0,
}
`)
})
})
describe('fromTupleList', () => {
test('default', () => {
const tupleList = [
['0x01', '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c', '0x28'],
['0x03', '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c', '0x14'],
] as const satisfies Authorization.TupleList
const authorization = Authorization.fromTupleList(tupleList)
expectTypeOf(authorization).toMatchTypeOf<Authorization.List<false>>()
expect(authorization).toMatchInlineSnapshot(`
[
{
"address": "0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"chainId": 1,
"nonce": 40n,
},
{
"address": "0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"chainId": 3,
"nonce": 20n,
},
]
`)
})
test('behavior: signature', () => {
const tupleList = [
[
'0x05',
'0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
'0x2a',
'0x',
'0x01',
'0x02',
],
[
'0x02',
'0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
'0x2b',
'0x',
'0x04',
'0x05',
],
] as const satisfies Authorization.TupleList
const authorization = Authorization.fromTupleList(tupleList)
expect(authorization).toMatchInlineSnapshot(`
[
{
"address": "0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"chainId": 5,
"nonce": 42n,
"r": "0x0000000000000000000000000000000000000000000000000000000000000001",
"s": "0x0000000000000000000000000000000000000000000000000000000000000002",
"yParity": 0,
},
{
"address": "0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"chainId": 2,
"nonce": 43n,
"r": "0x0000000000000000000000000000000000000000000000000000000000000004",
"s": "0x0000000000000000000000000000000000000000000000000000000000000005",
"yParity": 0,
},
]
`)
})
})
describe('getSignPayload', () => {
test('default', () => {
expect(
Authorization.getSignPayload({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 1,
nonce: 40n,
}),
).toMatchInlineSnapshot(
`"0x5919da563810a99caf657d42bd10905adbd28b3b89b8a4577efa471e5e4b3914"`,
)
expect(
Authorization.getSignPayload({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 69,
nonce: 420n,
}),
).toMatchInlineSnapshot(
`"0x7bdd120f6437316be99b11232d472bb0209d20d7c564f4dfbad855189e830b15"`,
)
})
})
describe('hash', () => {
test('default', () => {
expect(
Authorization.hash({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 1,
nonce: 40n,
}),
).toMatchInlineSnapshot(
`"0x5919da563810a99caf657d42bd10905adbd28b3b89b8a4577efa471e5e4b3914"`,
)
expect(
Authorization.hash({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 69,
nonce: 420n,
}),
).toMatchInlineSnapshot(
`"0x7bdd120f6437316be99b11232d472bb0209d20d7c564f4dfbad855189e830b15"`,
)
})
test('options: presign equals getSignPayload', () => {
const authorization = {
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 1,
nonce: 40n,
} as const
const payload = Authorization.getSignPayload(authorization)
const hash_presign = Authorization.hash(authorization, { presign: true })
expect(hash_presign).toEqual(payload)
})
test('behavior: signed vs presign', () => {
const authorization = Authorization.from({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 1,
nonce: 40n,
})
const signature = Secp256k1.sign({
payload: Authorization.getSignPayload(authorization),
privateKey: accounts[0].privateKey,
})
const signed = Authorization.from(authorization, { signature })
const hash_default = Authorization.hash(signed)
const hash_presign = Authorization.hash(signed, { presign: true })
expect(hash_default).not.toEqual(hash_presign)
expect(hash_presign).toEqual(Authorization.getSignPayload(authorization))
expect(
Secp256k1.recoverAddress({
payload: Authorization.hash(authorization, { presign: true }),
signature,
}),
).toEqual(accounts[0].address)
})
})
describe('toRpc', () => {
test('default', () => {
expect(
Authorization.toRpc({
address: '0x0000000000000000000000000000000000000000',
chainId: 1,
nonce: 1n,
r: '0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d',
s: '0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540',
yParity: 0,
}),
).toMatchInlineSnapshot(`
{
"address": "0x0000000000000000000000000000000000000000",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d",
"s": "0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540",
"yParity": "0x0",
}
`)
})
test('numberish inputs', () => {
const fromBigint = Authorization.toRpc({
address: '0x0000000000000000000000000000000000000000',
chainId: 1,
nonce: 1n,
r: '0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d',
s: '0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540',
yParity: 0,
})
const fromNumber = Authorization.toRpc({
address: '0x0000000000000000000000000000000000000000',
chainId: 1,
nonce: 1,
r: '0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d',
s: '0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540',
yParity: 0,
})
const fromHex = Authorization.toRpc({
address: '0x0000000000000000000000000000000000000000',
chainId: '0x1',
nonce: '0x1',
r: '0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d',
s: '0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540',
yParity: 0,
})
expect(fromBigint).toEqual(fromNumber)
expect(fromBigint).toEqual(fromHex)
})
})
describe('toRpcList', () => {
test('default', () => {
expect(
Authorization.toRpcList([
{
address: '0x0000000000000000000000000000000000000000',
chainId: 1,
nonce: 1n,
r: '0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d',
s: '0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540',
yParity: 0,
},
]),
).toMatchInlineSnapshot(`
[
{
"address": "0x0000000000000000000000000000000000000000",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d",
"s": "0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540",
"yParity": "0x0",
},
]
`)
})
})
describe('toTuple', () => {
test('default', () => {
{
const authorization = Authorization.from({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 1,
nonce: 40n,
})
const tuple = Authorization.toTuple(authorization)
expect(tuple).toMatchInlineSnapshot(`
[
"0x1",
"0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"0x28",
]
`)
}
{
const authorization = Authorization.from({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 1,
nonce: 40n,
r: '0x0000000000000000000000000000000000000000000000000000000000000001',
s: '0x0000000000000000000000000000000000000000000000000000000000000002',
yParity: 0,
})
const tuple = Authorization.toTuple(authorization)
expect(tuple).toMatchInlineSnapshot(`
[
"0x1",
"0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"0x28",
"0x",
"0x1",
"0x2",
]
`)
}
{
const authorization = Authorization.from({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 0,
nonce: 0n,
})
const tuple = Authorization.toTuple(authorization)
expect(tuple).toMatchInlineSnapshot(`
[
"0x",
"0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"0x",
]
`)
}
})
})
describe('toTupleList', () => {
test('default', () => {
{
const tuple = Authorization.toTupleList([])
expect(tuple).toMatchInlineSnapshot('[]')
}
{
const authorization_1 = Authorization.from({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 1,
nonce: 40n,
})
const authorization_2 = Authorization.from({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 3,
nonce: 20n,
})
const tuple = Authorization.toTupleList([
authorization_1,
authorization_2,
])
expect(tuple).toMatchInlineSnapshot(`
[
[
"0x1",
"0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"0x28",
],
[
"0x3",
"0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"0x14",
],
]
`)
}
{
const authorization_3 = Authorization.from({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 5,
nonce: 42n,
r: '0x0000000000000000000000000000000000000000000000000000000000000001',
s: '0x0000000000000000000000000000000000000000000000000000000000000002',
yParity: 0,
})
const authorization_4 = Authorization.from({
address: '0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c',
chainId: 2,
nonce: 43n,
r: '0x0000000000000000000000000000000000000000000000000000000000000004',
s: '0x0000000000000000000000000000000000000000000000000000000000000005',
yParity: 0,
})
const tuple = Authorization.toTupleList([
authorization_3,
authorization_4,
])
expect(tuple).toMatchInlineSnapshot(`
[
[
"0x5",
"0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"0x2a",
"0x",
"0x1",
"0x2",
],
[
"0x2",
"0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c",
"0x2b",
"0x",
"0x4",
"0x5",
],
]
`)
}
})
test('behavior: undefined input returns empty', () => {
// Explicitly call without an argument to exercise optional parameter path.
const tuple = Authorization.toTupleList()
expect(tuple).toMatchInlineSnapshot('[]')
})
})
test('exports', () => {
expect(Object.keys(Authorization)).toMatchInlineSnapshot(`
[
"from",
"fromRpc",
"fromRpcList",
"fromTuple",
"fromTupleList",
"getSignPayload",
"hash",
"toRpc",
"toRpcList",
"toTuple",
"toTupleList",
]
`)
})