Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions src/tempo/Decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2308,6 +2308,76 @@ export type Decorator<
approveSync: (
parameters: tokenActions.approveSync.Parameters<chain, account>,
) => Promise<tokenActions.approveSync.ReturnValue>
/**
* Sets allowance via EIP-2612 permit signature.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { tempo } from 'viem/chains'
* import { tempoActions } from 'viem/tempo'
*
* const client = createClient({
* account: privateKeyToAccount('0x...'),
* chain: tempo
* transport: http(),
* }).extend(tempoActions())
*
* const hash = await client.token.permit({
* token: '0x...',
* owner: '0x...',
* spender: '0x...',
* value: 100n,
* deadline: 1710000000n,
* v: 27,
* r: '0x...',
* s: '0x...',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The transaction hash.
*/
permit: (
parameters: tokenActions.permit.Parameters<chain, account>,
) => Promise<tokenActions.permit.ReturnValue>
/**
* Sets allowance via EIP-2612 permit signature.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { tempo } from 'viem/chains'
* import { tempoActions } from 'viem/tempo'
*
* const client = createClient({
* account: privateKeyToAccount('0x...'),
* chain: tempo
* transport: http(),
* }).extend(tempoActions())
*
* const result = await client.token.permitSync({
* token: '0x...',
* owner: '0x...',
* spender: '0x...',
* value: 100n,
* deadline: 1710000000n,
* v: 27,
* r: '0x...',
* s: '0x...',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The transaction receipt and event data.
*/
permitSync: (
parameters: tokenActions.permitSync.Parameters<chain, account>,
) => Promise<tokenActions.permitSync.ReturnValue>
/**
* Burns TIP20 tokens from a blocked address.
*
Expand Down Expand Up @@ -2601,6 +2671,86 @@ export type Decorator<
getBalance: (
parameters: tokenActions.getBalance.Parameters<account>,
) => Promise<tokenActions.getBalance.ReturnValue>
/**
* Gets the EIP-712 domain separator for a TIP20 token.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { tempoActions } from 'viem/tempo'
*
* const client = createClient({
* chain: tempo
* transport: http(),
* }).extend(tempoActions())
*
* const domainSeparator = await client.token.getDomainSeparator({
* token: '0x...',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The EIP-712 domain separator.
*/
getDomainSeparator: (
parameters: tokenActions.getDomainSeparator.Parameters,
) => Promise<tokenActions.getDomainSeparator.ReturnValue>
/**
* Gets the EIP-2612 nonce for an account on a TIP20 token.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { privateKeyToAccount } from 'viem/accounts'
* import { tempo } from 'viem/chains'
* import { tempoActions } from 'viem/tempo'
*
* const client = createClient({
* account: privateKeyToAccount('0x...'),
* chain: tempo
* transport: http(),
* }).extend(tempoActions())
*
* const nonce = await client.token.getNonce({
* token: '0x...',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The nonce.
*/
getNonce: (
parameters: tokenActions.getNonce.Parameters<account>,
) => Promise<tokenActions.getNonce.ReturnValue>
/**
* Gets the opted-in supply for rewards on a TIP20 token.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { tempoActions } from 'viem/tempo'
*
* const client = createClient({
* chain: tempo
* transport: http(),
* }).extend(tempoActions())
*
* const optedInSupply = await client.token.getOptedInSupply({
* token: '0x...',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The opted-in supply.
*/
getOptedInSupply: (
parameters: tokenActions.getOptedInSupply.Parameters,
) => Promise<tokenActions.getOptedInSupply.ReturnValue>
/**
* Gets TIP20 token metadata including name, symbol, currency, decimals, and total supply.
*
Expand Down Expand Up @@ -4414,6 +4564,8 @@ export function decorator() {
approve: (parameters) => tokenActions.approve(client, parameters),
approveSync: (parameters) =>
tokenActions.approveSync(client, parameters),
permit: (parameters) => tokenActions.permit(client, parameters),
permitSync: (parameters) => tokenActions.permitSync(client, parameters),
burnBlocked: (parameters) =>
tokenActions.burnBlocked(client, parameters),
burnBlockedSync: (parameters) =>
Expand All @@ -4429,8 +4581,13 @@ export function decorator() {
getAllowance: (parameters) =>
tokenActions.getAllowance(client, parameters),
getBalance: (parameters) => tokenActions.getBalance(client, parameters),
getDomainSeparator: (parameters) =>
tokenActions.getDomainSeparator(client, parameters),
getMetadata: (parameters) =>
tokenActions.getMetadata(client, parameters),
getNonce: (parameters) => tokenActions.getNonce(client, parameters),
getOptedInSupply: (parameters) =>
tokenActions.getOptedInSupply(client, parameters),
getRoleAdmin: (parameters) =>
tokenActions.getRoleAdmin(client, parameters),
hasRole: (parameters) => tokenActions.hasRole(client, parameters),
Expand Down
119 changes: 118 additions & 1 deletion src/tempo/actions/token.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { setTimeout } from 'node:timers/promises'
import { Hex } from 'ox'
import { TokenId, TokenRole } from 'ox/tempo'
import { parseUnits } from 'viem'
import { parseSignature, parseUnits } from 'viem'
import { getCode, writeContractSync } from 'viem/actions'
import { Abis, Addresses, TokenIds } from 'viem/tempo'
import { describe, expect, test } from 'vitest'
Expand Down Expand Up @@ -218,6 +218,84 @@ describe('approve', () => {
})
})

describe('permit', () => {
test('default', async () => {
const { token } = await actions.token.createSync(client, {
currency: 'USD',
name: 'Permit Test Token',
symbol: 'PERMIT',
})

const { name } = await actions.token.getMetadata(client, {
token,
})
const nonceBefore = await actions.token.getNonce(client, {
token,
})
const value = parseUnits('125', 6)
const deadline = BigInt(Math.floor(Date.now() / 1000) + 60 * 60)

if (!client.chain) throw new Error('chain is required.')

const signature = await account.signTypedData({
domain: {
chainId: client.chain.id,
name,
verifyingContract: token,
version: '1',
},
message: {
deadline,
nonce: nonceBefore,
owner: account.address,
spender: account2.address,
value,
},
primaryType: 'Permit',
types: {
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
},
})
const { r, s, v, yParity } = parseSignature(signature)

const { receipt, ...result } = await actions.token.permitSync(client, {
deadline,
owner: account.address,
r,
s,
spender: account2.address,
token,
v: Number(v ?? BigInt(27 + yParity)),
value,
})
expect(receipt).toBeDefined()
expect(result).toMatchInlineSnapshot(`
{
"amount": 125000000n,
"owner": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"spender": "0x8C8d35429F74ec245F8Ef2f4Fd1e551cFF97d650",
}
`)

const allowance = await actions.token.getAllowance(client, {
spender: account2.address,
token,
})
expect(allowance).toBe(value)

const nonceAfter = await actions.token.getNonce(client, {
token,
})
expect(nonceAfter).toBe(nonceBefore + 1n)
})
})

describe('create', () => {
test('default', async () => {
const { receipt, salt, token, tokenId, ...result } =
Expand Down Expand Up @@ -312,6 +390,45 @@ describe('getBalance', () => {
})
})

describe('getDomainSeparator', () => {
test('default', async () => {
const domainSeparator = await actions.token.getDomainSeparator(client, {
token: addresses.alphaUsd,
})

expect(domainSeparator).toMatch(/^0x[0-9a-fA-F]{64}$/)
})
})

describe('getNonce', () => {
test('default', async () => {
const nonce = await actions.token.getNonce(client, {
token: addresses.alphaUsd,
})

expect(nonce).toBeGreaterThanOrEqual(0n)
})

test('behavior: explicit account', async () => {
const nonce = await actions.token.getNonce(client, {
account: account2.address,
token: addresses.alphaUsd,
})

expect(nonce).toBeGreaterThanOrEqual(0n)
})
})

describe('getOptedInSupply', () => {
test('default', async () => {
const optedInSupply = await actions.token.getOptedInSupply(client, {
token: addresses.alphaUsd,
})

expect(optedInSupply).toBeGreaterThanOrEqual(0n)
})
})

describe('getMetadata', () => {
test('default', async () => {
const metadata = await actions.token.getMetadata(client, {
Expand Down
Loading