viem
Version:
753 lines • 22.5 kB
JavaScript
import { parseAccount } from '../../accounts/utils/parseAccount.js';
import { readContract } from '../../actions/public/readContract.js';
import { watchContractEvent } from '../../actions/public/watchContractEvent.js';
import { writeContract } from '../../actions/wallet/writeContract.js';
import { writeContractSync } from '../../actions/wallet/writeContractSync.js';
import { zeroAddress } from '../../constants/address.js';
import { parseEventLogs } from '../../utils/abi/parseEventLogs.js';
import { isAddressEqual } from '../../utils/index.js';
import * as Abis from '../Abis.js';
import * as Addresses from '../Addresses.js';
import { defineCall } from '../internal/utils.js';
/** @internal */
const policyTypes = ['whitelist', 'blacklist'];
/** @internal Built-in TIP-403 policy id that rejects everything. */
const rejectAllPolicyId = 0n;
/** @internal Built-in TIP-403 policy id that allows everything. */
const allowAllPolicyId = 1n;
/** @internal */
const blockedReasons = ['none', 'tokenFilter', 'receivePolicy'];
/**
* Burns the funds backing a blocked receipt.
*
* Requires the caller to hold the token's `BURN_BLOCKED_ROLE`, and is only
* valid when the receipt's policy subject is currently unauthorized as a sender
* under the token's TIP-403 policy.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
* import { privateKeyToAccount } from 'viem/accounts'
*
* const client = createClient({
* account: privateKeyToAccount('0x...'),
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const hash = await Actions.receivePolicy.burn(client, {
* receipt: '0x...',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The transaction hash.
*/
export async function burn(client, parameters) {
return burn.inner(writeContract, client, parameters);
}
(function (burn) {
/** @internal */
async function inner(action, client, parameters) {
const { account = client.account, chain = client.chain, receipt, ...rest } = parameters;
if (!account)
throw new Error('`account` is required');
const call = burn.call({ receipt });
return action(client, {
...rest,
account,
chain,
...call,
});
}
burn.inner = inner;
/**
* Defines a call to the `burnBlockedReceipt` function.
*
* @param args - Arguments.
* @returns The call.
*/
function call(args) {
const { receipt } = args;
return defineCall({
address: Addresses.receivePolicyGuard,
abi: Abis.receivePolicyGuard,
functionName: 'burnBlockedReceipt',
args: [receipt],
});
}
burn.call = call;
/**
* Extracts the `ReceiptBurned` event from logs.
*
* @param logs - The logs.
* @returns The `ReceiptBurned` event.
*/
function extractEvent(logs) {
const [log] = parseEventLogs({
abi: Abis.receivePolicyGuard,
logs,
eventName: 'ReceiptBurned',
strict: true,
});
if (!log)
throw new Error('`ReceiptBurned` event not found.');
return log;
}
burn.extractEvent = extractEvent;
})(burn || (burn = {}));
/**
* Burns the funds backing a blocked receipt and waits for the receipt.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
* import { privateKeyToAccount } from 'viem/accounts'
*
* const client = createClient({
* account: privateKeyToAccount('0x...'),
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const { receipt, ...result } = await Actions.receivePolicy.burnSync(client, {
* receipt: '0x...',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The transaction receipt and event data.
*/
export async function burnSync(client, parameters) {
const { throwOnReceiptRevert = true, ...rest } = parameters;
const receipt = await burn.inner(writeContractSync, client, {
...rest,
throwOnReceiptRevert,
});
const { args } = burn.extractEvent(receipt.logs);
return {
...args,
receipt,
};
}
/**
* Claims blocked funds for a receipt, releasing them to a destination.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
* import { privateKeyToAccount } from 'viem/accounts'
*
* const client = createClient({
* account: privateKeyToAccount('0x...'),
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const hash = await Actions.receivePolicy.claim(client, {
* to: '0x...',
* receipt: '0x...',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The transaction hash.
*/
export async function claim(client, parameters) {
return claim.inner(writeContract, client, parameters);
}
(function (claim) {
/** @internal */
async function inner(action, client, parameters) {
const { account = client.account, chain = client.chain, to, receipt, ...rest } = parameters;
if (!account)
throw new Error('`account` is required');
const call = claim.call({ to, receipt });
return action(client, {
...rest,
account,
chain,
...call,
});
}
claim.inner = inner;
/**
* Defines a call to the `claim` function.
*
* @param args - Arguments.
* @returns The call.
*/
function call(args) {
const { to, receipt } = args;
return defineCall({
address: Addresses.receivePolicyGuard,
abi: Abis.receivePolicyGuard,
functionName: 'claim',
args: [to, receipt],
});
}
claim.call = call;
/**
* Extracts the `ReceiptClaimed` event from logs.
*
* @param logs - The logs.
* @returns The `ReceiptClaimed` event.
*/
function extractEvent(logs) {
const [log] = parseEventLogs({
abi: Abis.receivePolicyGuard,
logs,
eventName: 'ReceiptClaimed',
strict: true,
});
if (!log)
throw new Error('`ReceiptClaimed` event not found.');
return log;
}
claim.extractEvent = extractEvent;
})(claim || (claim = {}));
/**
* Claims blocked funds for a receipt and waits for the receipt.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
* import { privateKeyToAccount } from 'viem/accounts'
*
* const client = createClient({
* account: privateKeyToAccount('0x...'),
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const { receipt, ...result } = await Actions.receivePolicy.claimSync(client, {
* to: '0x...',
* receipt: '0x...',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The transaction receipt and event data.
*/
export async function claimSync(client, parameters) {
const { throwOnReceiptRevert = true, ...rest } = parameters;
const receipt = await claim.inner(writeContractSync, client, {
...rest,
throwOnReceiptRevert,
});
const { args } = claim.extractEvent(receipt.logs);
return {
...args,
receipt,
};
}
/**
* Gets the receive policy configured for an account.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
*
* const client = createClient({
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const policy = await Actions.receivePolicy.get(client, {
* account: '0x...',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The receive policy.
*/
export async function get(client, parameters) {
const { account: account_ = client.account, ...rest } = parameters;
if (!account_)
throw new Error('`account` is required.');
const account = parseAccount(account_);
const [hasReceivePolicy, senderPolicyId, senderPolicyType, tokenPolicyId, tokenPolicyType, recoveryAuthority,] = await readContract(client, {
...rest,
account: null,
...get.call({ account: account.address }),
});
return {
hasReceivePolicy,
senderPolicyId: toPolicyRef(senderPolicyId),
senderPolicyType: policyTypes[senderPolicyType] ?? 'whitelist',
tokenPolicyId: toPolicyRef(tokenPolicyId),
tokenPolicyType: policyTypes[tokenPolicyType] ?? 'whitelist',
claimer: toClaimer(recoveryAuthority, account.address),
recoveryAuthority,
};
}
(function (get) {
/**
* Defines a call to the `receivePolicy` function.
*
* @param args - Arguments.
* @returns The call.
*/
function call(args) {
const { account } = args;
return defineCall({
address: Addresses.tip403Registry,
abi: Abis.tip403Registry,
functionName: 'receivePolicy',
args: [account],
});
}
get.call = call;
})(get || (get = {}));
/**
* Gets the blocked balance for an encoded receipt.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
*
* const client = createClient({
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const amount = await Actions.receivePolicy.getBlockedBalance(client, {
* receipt: '0x...',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The blocked amount for the receipt.
*/
export async function getBlockedBalance(client, parameters) {
const { receipt, ...rest } = parameters;
return readContract(client, {
...rest,
...getBlockedBalance.call({ receipt }),
});
}
(function (getBlockedBalance) {
/**
* Defines a call to the `balanceOf` function.
*
* @param args - Arguments.
* @returns The call.
*/
function call(args) {
const { receipt } = args;
return defineCall({
address: Addresses.receivePolicyGuard,
abi: Abis.receivePolicyGuard,
functionName: 'balanceOf',
args: [receipt],
});
}
getBlockedBalance.call = call;
})(getBlockedBalance || (getBlockedBalance = {}));
/**
* Sets the receive policy for the calling account.
*
* A receive policy controls which TIP-20 tokens and which senders an account
* accepts. Inbound transfers and mints that violate the policy are not
* reverted – instead the funds are redirected to the `ReceivePolicyGuard` and
* can be reclaimed later (see {@link claim}).
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
* import { privateKeyToAccount } from 'viem/accounts'
*
* const client = createClient({
* account: privateKeyToAccount('0x...'),
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const hash = await Actions.receivePolicy.set(client, {
* senderPolicyId: 'allow-all',
* tokenPolicyId: 'allow-all',
* claimer: 'self',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The transaction hash.
*/
export async function set(client, parameters) {
return set.inner(writeContract, client, parameters);
}
(function (set) {
/** @internal */
async function inner(action, client, parameters) {
const { account = client.account, chain = client.chain, senderPolicyId = 'allow-all', tokenPolicyId = 'allow-all', claimer = 'sender', ...rest } = parameters;
if (!account)
throw new Error('`account` is required');
const address = parseAccount(account).address;
const recoveryAuthority = resolveClaimer(claimer, address);
const call = set.call({
senderPolicyId: resolvePolicyRef(senderPolicyId),
tokenFilterId: resolvePolicyRef(tokenPolicyId),
recoveryAuthority,
});
return action(client, {
...rest,
account,
chain,
...call,
});
}
set.inner = inner;
/**
* Defines a call to the `setReceivePolicy` function.
*
* @param args - Arguments.
* @returns The call.
*/
function call(args) {
const { senderPolicyId, tokenFilterId, recoveryAuthority } = args;
return defineCall({
address: Addresses.tip403Registry,
abi: Abis.tip403Registry,
functionName: 'setReceivePolicy',
args: [senderPolicyId, tokenFilterId, recoveryAuthority],
});
}
set.call = call;
/**
* Extracts the `ReceivePolicyUpdated` event from logs.
*
* @param logs - The logs.
* @returns The `ReceivePolicyUpdated` event.
*/
function extractEvent(logs) {
const [log] = parseEventLogs({
abi: Abis.tip403Registry,
logs,
eventName: 'ReceivePolicyUpdated',
strict: true,
});
if (!log)
throw new Error('`ReceivePolicyUpdated` event not found.');
return log;
}
set.extractEvent = extractEvent;
})(set || (set = {}));
/**
* Sets the receive policy for the calling account and waits for the receipt.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
* import { privateKeyToAccount } from 'viem/accounts'
*
* const client = createClient({
* account: privateKeyToAccount('0x...'),
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const { receipt, ...result } = await Actions.receivePolicy.setSync(client, {
* senderPolicyId: 'allow-all',
* tokenPolicyId: 'allow-all',
* claimer: 'self',
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns The transaction receipt and event data.
*/
export async function setSync(client, parameters) {
const { throwOnReceiptRevert = true, ...rest } = parameters;
const receipt = await set.inner(writeContractSync, client, {
...rest,
throwOnReceiptRevert,
});
const { tokenFilterId, ...args } = set.extractEvent(receipt.logs).args;
return {
...args,
senderPolicyId: toPolicyRef(args.senderPolicyId),
tokenPolicyId: toPolicyRef(tokenFilterId),
claimer: toClaimer(args.recoveryAuthority, args.account),
receipt,
};
}
/**
* Checks whether a transfer or mint to a receiver is allowed by the receiver's
* receive policy.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
*
* const client = createClient({
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const { authorized, blockedReason } = await Actions.receivePolicy.validate(
* client,
* {
* token: '0x...',
* sender: '0x...',
* receiver: '0x...',
* },
* )
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns Whether the transfer is authorized and, if not, why.
*/
export async function validate(client, parameters) {
const { token, sender, receiver, ...rest } = parameters;
const [authorized, blockedReason] = await readContract(client, {
...rest,
...validate.call({ token, sender, receiver }),
});
return {
authorized,
blockedReason: blockedReasons[blockedReason] ?? 'none',
};
}
(function (validate) {
/**
* Defines a call to the `validateReceivePolicy` function.
*
* @param args - Arguments.
* @returns The call.
*/
function call(args) {
const { token, sender, receiver } = args;
return defineCall({
address: Addresses.tip403Registry,
abi: Abis.tip403Registry,
functionName: 'validateReceivePolicy',
args: [token, sender, receiver],
});
}
validate.call = call;
})(validate || (validate = {}));
/**
* Watches for blocked transfer events.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
*
* const client = createClient({
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const unwatch = Actions.receivePolicy.watchBlocked(client, {
* onBlocked: (args, log) => {
* console.log('Transfer blocked:', args)
* },
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns A function to unsubscribe from the event.
*/
export function watchBlocked(client, parameters) {
const { onBlocked, ...rest } = parameters;
return watchContractEvent(client, {
...rest,
address: Addresses.receivePolicyGuard,
abi: Abis.receivePolicyGuard,
eventName: 'TransferBlocked',
onLogs: (logs) => {
for (const log of logs) {
const { receipt, ...args } = log.args;
onBlocked({ ...args, claimReceipt: receipt }, log);
}
},
strict: true,
});
}
/**
* Watches for receipt burned events.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
*
* const client = createClient({
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const unwatch = Actions.receivePolicy.watchBurned(client, {
* onBurned: (args, log) => {
* console.log('Receipt burned:', args)
* },
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns A function to unsubscribe from the event.
*/
export function watchBurned(client, parameters) {
const { onBurned, ...rest } = parameters;
return watchContractEvent(client, {
...rest,
address: Addresses.receivePolicyGuard,
abi: Abis.receivePolicyGuard,
eventName: 'ReceiptBurned',
onLogs: (logs) => {
for (const log of logs)
onBurned(log.args, log);
},
strict: true,
});
}
/**
* Watches for receipt claimed events.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
*
* const client = createClient({
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const unwatch = Actions.receivePolicy.watchClaimed(client, {
* onClaimed: (args, log) => {
* console.log('Receipt claimed:', args)
* },
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns A function to unsubscribe from the event.
*/
export function watchClaimed(client, parameters) {
const { onClaimed, ...rest } = parameters;
return watchContractEvent(client, {
...rest,
address: Addresses.receivePolicyGuard,
abi: Abis.receivePolicyGuard,
eventName: 'ReceiptClaimed',
onLogs: (logs) => {
for (const log of logs)
onClaimed(log.args, log);
},
strict: true,
});
}
/**
* Watches for receive policy update events.
*
* @example
* ```ts
* import { createClient, http } from 'viem'
* import { tempo } from 'viem/chains'
* import { Actions } from 'viem/tempo'
*
* const client = createClient({
* chain: tempo.extend({ feeToken: '0x20c0000000000000000000000000000000000001' }),
* transport: http(),
* })
*
* const unwatch = Actions.receivePolicy.watchUpdated(client, {
* onUpdated: (args, log) => {
* console.log('Receive policy updated:', args)
* },
* })
* ```
*
* @param client - Client.
* @param parameters - Parameters.
* @returns A function to unsubscribe from the event.
*/
export function watchUpdated(client, parameters) {
const { onUpdated, ...rest } = parameters;
return watchContractEvent(client, {
...rest,
address: Addresses.tip403Registry,
abi: Abis.tip403Registry,
eventName: 'ReceivePolicyUpdated',
onLogs: (logs) => {
for (const log of logs) {
const { tokenFilterId, ...args } = log.args;
onUpdated({
...args,
senderPolicyId: toPolicyRef(args.senderPolicyId),
tokenPolicyId: toPolicyRef(tokenFilterId),
claimer: toClaimer(args.recoveryAuthority, args.account),
}, log);
}
},
strict: true,
});
}
/** @internal */
function resolvePolicyRef(ref) {
if (ref === 'reject-all')
return rejectAllPolicyId;
if (ref === 'allow-all')
return allowAllPolicyId;
return ref;
}
/** @internal */
function toPolicyRef(id) {
if (id === rejectAllPolicyId)
return 'reject-all';
if (id === allowAllPolicyId)
return 'allow-all';
return id;
}
/** @internal */
function resolveClaimer(claimer, self) {
if (claimer === 'sender')
return zeroAddress;
if (claimer === 'self')
return self;
return claimer;
}
/** @internal */
function toClaimer(recoveryAuthority, account) {
if (recoveryAuthority === zeroAddress)
return 'sender';
if (isAddressEqual(recoveryAuthority, account))
return 'self';
return recoveryAuthority;
}
//# sourceMappingURL=receivePolicy.js.map