Skip to content

Commit

Permalink
Add borsh types/serialization (#6)
Browse files Browse the repository at this point in the history
* Add borsh types/serialization

* Add borsher

* Add forgotten type
  • Loading branch information
r-near authored Dec 13, 2024
1 parent 2dc3ea4 commit b225e76
Show file tree
Hide file tree
Showing 6 changed files with 622 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"license": "MIT",
"dependencies": {
"@solana/web3.js": "^1.95.5",
"borsher": "^3.5.0",
"ethers": "^6.13.4",
"near-api-js": "^5.0.1"
},
Expand Down
16 changes: 16 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

243 changes: 243 additions & 0 deletions src/types/locker.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
import { borshDeserialize } from "borsher"
import { describe, expect, test } from "vitest"
import {
type BindTokenArgs,
BindTokenArgsSchema,
ChainKind,
type ClaimFeeArgs,
ClaimFeeArgsSchema,
type DeployTokenArgs,
DeployTokenArgsSchema,
type FinTransferArgs,
FinTransferArgsSchema,
type StorageDepositAction,
StorageDepositActionSchema,
serializeBindTokenArgs,
serializeClaimFeeArgs,
serializeDeployTokenArgs,
serializeFinTransferArgs,
serializeStorageDepositAction,
} from "./locker"

describe("Chain Kind Types", () => {
describe("StorageDepositAction", () => {
test("should serialize and deserialize with storage deposit amount", () => {
const action: StorageDepositAction = {
token_id: "token.near",
account_id: "user.near",
storage_deposit_amount: 1000000n,
}

const serialized = serializeStorageDepositAction(action)
const deserialized = borshDeserialize(StorageDepositActionSchema, serialized)

expect(deserialized).toEqual(action)
})

test("should handle null storage deposit amount", () => {
const action: StorageDepositAction = {
token_id: "token.near",
account_id: "user.near",
storage_deposit_amount: null,
}

const serialized = serializeStorageDepositAction(action)
const deserialized = borshDeserialize(StorageDepositActionSchema, serialized)

expect(deserialized).toEqual(action)
})

test("should handle long account IDs", () => {
const action: StorageDepositAction = {
token_id: "very.long.token.name.near",
account_id: "very.long.account.name.near",
storage_deposit_amount: 1000000n,
}

const serialized = serializeStorageDepositAction(action)
const deserialized = borshDeserialize(StorageDepositActionSchema, serialized)

expect(deserialized).toEqual(action)
})
})

describe("FinTransferArgs", () => {
test("should serialize and deserialize with multiple storage deposit actions", () => {
const args: FinTransferArgs = {
chain_kind: ChainKind.Near,
storage_deposit_actions: [
{
token_id: "token1.near",
account_id: "user1.near",
storage_deposit_amount: 1000000n,
},
{
token_id: "token2.near",
account_id: "user2.near",
storage_deposit_amount: null,
},
],
prover_args: new Uint8Array([1, 2, 3]),
}

const serialized = serializeFinTransferArgs(args)
const deserialized: FinTransferArgs = borshDeserialize(FinTransferArgsSchema, serialized)

expect({
...deserialized,
prover_args: Uint8Array.from(deserialized.prover_args),
}).toEqual(args)
})

test("should handle empty storage deposit actions array", () => {
const args: FinTransferArgs = {
chain_kind: ChainKind.Near,
storage_deposit_actions: [],
prover_args: new Uint8Array([1, 2, 3]),
}

const serialized = serializeFinTransferArgs(args)
const deserialized: FinTransferArgs = borshDeserialize(FinTransferArgsSchema, serialized)

expect({
...deserialized,
prover_args: Uint8Array.from(deserialized.prover_args),
}).toEqual(args)
})

test("should handle different chain kinds", () => {
const chainKinds = [
ChainKind.Eth,
ChainKind.Near,
ChainKind.Sol,
ChainKind.Arb,
ChainKind.Base,
]

for (const chainKind of chainKinds) {
const args: FinTransferArgs = {
chain_kind: chainKind,
storage_deposit_actions: [],
prover_args: new Uint8Array([1, 2, 3]),
}

const serialized = serializeFinTransferArgs(args)
const deserialized: FinTransferArgs = borshDeserialize(FinTransferArgsSchema, serialized)
expect({
...deserialized,
prover_args: Uint8Array.from(deserialized.prover_args),
}).toEqual(args)
}
})
})

describe("ClaimFeeArgs", () => {
test("should serialize and deserialize with different chain kinds", () => {
const args: ClaimFeeArgs = {
chain_kind: ChainKind.Eth,
prover_args: new Uint8Array([1, 2, 3]),
}

const serialized = serializeClaimFeeArgs(args)
const deserialized: ClaimFeeArgs = borshDeserialize(ClaimFeeArgsSchema, serialized)

expect({
...deserialized,
prover_args: Uint8Array.from(deserialized.prover_args),
}).toEqual(args)
})

test("should handle empty prover args", () => {
const args: ClaimFeeArgs = {
chain_kind: ChainKind.Sol,
prover_args: new Uint8Array([]),
}

const serialized = serializeClaimFeeArgs(args)
const deserialized: ClaimFeeArgs = borshDeserialize(ClaimFeeArgsSchema, serialized)

expect({
...deserialized,
prover_args: Uint8Array.from(deserialized.prover_args),
}).toEqual(args)
})
})

describe("BindTokenArgs", () => {
test("should serialize and deserialize correctly", () => {
const args: BindTokenArgs = {
chain_kind: ChainKind.Eth,
prover_args: new Uint8Array([1, 2, 3]),
}

const serialized = serializeBindTokenArgs(args)
const deserialized: BindTokenArgs = borshDeserialize(BindTokenArgsSchema, serialized)
expect({
...deserialized,
prover_args: Uint8Array.from(deserialized.prover_args),
}).toEqual(args)
})
})

describe("DeployTokenArgs", () => {
test("should serialize and deserialize correctly", () => {
const args: DeployTokenArgs = {
chain_kind: ChainKind.Base,
prover_args: new Uint8Array([1, 2, 3]),
}

const serialized = serializeDeployTokenArgs(args)
const deserialized: DeployTokenArgs = borshDeserialize(DeployTokenArgsSchema, serialized)
expect({
...deserialized,
prover_args: Uint8Array.from(deserialized.prover_args),
}).toEqual(args)
})
})

describe("Edge Cases", () => {
test("should handle large storage deposit amounts", () => {
const action: StorageDepositAction = {
token_id: "token.near",
account_id: "user.near",
storage_deposit_amount: BigInt("340282366920938463463374607431768211455"), // u128 max
}

const serialized = serializeStorageDepositAction(action)
const deserialized = borshDeserialize(StorageDepositActionSchema, serialized)

expect(deserialized).toEqual(action)
})

test("should handle large prover args", () => {
const largeProverArgs = new Uint8Array(1000).fill(1)
const args: FinTransferArgs = {
chain_kind: ChainKind.Near,
storage_deposit_actions: [],
prover_args: largeProverArgs,
}

const serialized = serializeFinTransferArgs(args)
const deserialized: FinTransferArgs = borshDeserialize(FinTransferArgsSchema, serialized)
expect({
...deserialized,
prover_args: Uint8Array.from(deserialized.prover_args),
}).toEqual(args)
})

test("should handle maximum length account IDs", () => {
// NEAR account IDs have a maximum length of 64 characters
const maxLengthAccountId = "a".repeat(64)
const action: StorageDepositAction = {
token_id: maxLengthAccountId,
account_id: maxLengthAccountId,
storage_deposit_amount: 1000000n,
}

const serialized = serializeStorageDepositAction(action)
const deserialized = borshDeserialize(StorageDepositActionSchema, serialized)

expect(deserialized).toEqual(action)
})
})
})
108 changes: 108 additions & 0 deletions src/types/locker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { BorshSchema, type Unit, borshSerialize } from "borsher"

// Basic type aliases
export type AccountId = string
export type U128 = bigint

export type ChainKind =
| { Eth: Unit }
| { Near: Unit }
| { Sol: Unit }
| { Arb: Unit }
| { Base: Unit }

export const ChainKind = {
Eth: { Eth: {} } as ChainKind,
Near: { Near: {} } as ChainKind,
Sol: { Sol: {} } as ChainKind,
Arb: { Arb: {} } as ChainKind,
Base: { Base: {} } as ChainKind,
} as const

export const ChainKindSchema = BorshSchema.Enum({
Eth: BorshSchema.Unit,
Near: BorshSchema.Unit,
Sol: BorshSchema.Unit,
Arb: BorshSchema.Unit,
Base: BorshSchema.Unit,
})

// StorageDepositAction type
export type StorageDepositAction = {
token_id: AccountId
account_id: AccountId
storage_deposit_amount: bigint | null
}

export const StorageDepositActionSchema = BorshSchema.Struct({
token_id: BorshSchema.String,
account_id: BorshSchema.String,
storage_deposit_amount: BorshSchema.Option(BorshSchema.u128),
})

// FinTransferArgs type
export type FinTransferArgs = {
chain_kind: ChainKind
storage_deposit_actions: StorageDepositAction[]
prover_args: Uint8Array
}

export const FinTransferArgsSchema = BorshSchema.Struct({
chain_kind: ChainKindSchema,
storage_deposit_actions: BorshSchema.Vec(StorageDepositActionSchema),
prover_args: BorshSchema.Vec(BorshSchema.u8),
})

// ClaimFeeArgs type
export type ClaimFeeArgs = {
chain_kind: ChainKind
prover_args: Uint8Array
}

export const ClaimFeeArgsSchema = BorshSchema.Struct({
chain_kind: ChainKindSchema,
prover_args: BorshSchema.Vec(BorshSchema.u8),
})

// BindTokenArgs type
export type BindTokenArgs = {
chain_kind: ChainKind
prover_args: Uint8Array
}

export const BindTokenArgsSchema = BorshSchema.Struct({
chain_kind: ChainKindSchema,
prover_args: BorshSchema.Vec(BorshSchema.u8),
})

// DeployTokenArgs type
export type DeployTokenArgs = {
chain_kind: ChainKind
prover_args: Uint8Array
}

export const DeployTokenArgsSchema = BorshSchema.Struct({
chain_kind: ChainKindSchema,
prover_args: BorshSchema.Vec(BorshSchema.u8),
})

// Serialization helper functions
export const serializeStorageDepositAction = (action: StorageDepositAction): Uint8Array => {
return borshSerialize(StorageDepositActionSchema, action)
}

export const serializeFinTransferArgs = (args: FinTransferArgs): Uint8Array => {
return borshSerialize(FinTransferArgsSchema, args)
}

export const serializeClaimFeeArgs = (args: ClaimFeeArgs): Uint8Array => {
return borshSerialize(ClaimFeeArgsSchema, args)
}

export const serializeBindTokenArgs = (args: BindTokenArgs): Uint8Array => {
return borshSerialize(BindTokenArgsSchema, args)
}

export const serializeDeployTokenArgs = (args: DeployTokenArgs): Uint8Array => {
return borshSerialize(DeployTokenArgsSchema, args)
}
Loading

0 comments on commit b225e76

Please sign in to comment.