@coinbase/cdp-sdk
Version:
SDK for interacting with the Coinbase Developer Platform Wallet API
86 lines (73 loc) • 2.87 kB
text/typescript
import { UserInputValidationError } from "../../../errors.js";
import { resolveTokenAddress } from "../../../spend-permissions/utils.js";
import type { SpendPermissionNetwork } from "../../../openapi-client/index.js";
import type { SpendPermission, SpendPermissionInput } from "../../../spend-permissions/types.js";
/**
* Generate a random salt using crypto.getRandomValues().
*
* @returns A random bigint salt.
*/
function generateRandomSalt(): bigint {
const randomBytes = new Uint8Array(32);
crypto.getRandomValues(randomBytes);
// Convert bytes to bigint
let result = 0n;
for (let i = 0; i < randomBytes.length; i++) {
result = (result << 8n) + BigInt(randomBytes[i]);
}
return result;
}
/**
* Resolve a spend permission input to a spend permission.
*
* @param spendPermissionInput - The spend permission input to resolve.
* @param network - The network to resolve the spend permission for.
*
* @returns The resolved spend permission.
*/
export function resolveSpendPermission(
spendPermissionInput: SpendPermissionInput,
network: SpendPermissionNetwork,
): SpendPermission {
// Validate that either period or periodInDays is provided, but not both
if (
spendPermissionInput.period !== undefined &&
spendPermissionInput.periodInDays !== undefined
) {
throw new UserInputValidationError(
"Cannot specify both 'period' and 'periodInDays'. Please provide only one.",
);
}
if (
spendPermissionInput.period === undefined &&
spendPermissionInput.periodInDays === undefined
) {
throw new UserInputValidationError(
"Must specify either 'period' (in seconds) or 'periodInDays'.",
);
}
// Convert periodInDays to period in seconds if provided
const period = spendPermissionInput.period ?? spendPermissionInput.periodInDays! * 24 * 60 * 60;
// Set defaults for start and end
const now = new Date();
const startDate = spendPermissionInput.start ?? now;
/*
* For end date default, we need to handle the max uint48 value carefully
* JavaScript Date max is around year 275760, but uint48 max (281474976710655) is much larger
* So we'll use the max uint48 value directly for end if no end date is provided
*/
const endDate = spendPermissionInput.end;
// Convert Date objects to seconds since epoch for the contract
const start = Math.floor(startDate.getTime() / 1000);
const end = endDate ? Math.floor(endDate.getTime() / 1000) : 281474976710655; // Max uint48 value (never expires)
const { periodInDays: _periodInDays, ...inputWithoutPeriodInDays } = spendPermissionInput;
return {
...inputWithoutPeriodInDays,
token: resolveTokenAddress(spendPermissionInput.token, network),
period,
start,
end,
salt: spendPermissionInput.salt ?? generateRandomSalt(),
extraData: spendPermissionInput.extraData ?? "0x",
};
}