"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toCoinbaseSmartAccount = toCoinbaseSmartAccount;
exports.signTypedData = signTypedData;
exports.sign = sign;
exports.toReplaySafeTypedData = toReplaySafeTypedData;
exports.toWebAuthnSignature = toWebAuthnSignature;
exports.wrapSignature = wrapSignature;
const Signature = require("ox/Signature");
const readContract_js_1 = require("../../../actions/public/readContract.js");
const base_js_1 = require("../../../errors/base.js");
const decodeFunctionData_js_1 = require("../../../utils/abi/decodeFunctionData.js");
const encodeAbiParameters_js_1 = require("../../../utils/abi/encodeAbiParameters.js");
const encodeFunctionData_js_1 = require("../../../utils/abi/encodeFunctionData.js");
const encodePacked_js_1 = require("../../../utils/abi/encodePacked.js");
const pad_js_1 = require("../../../utils/data/pad.js");
const size_js_1 = require("../../../utils/data/size.js");
const toHex_js_1 = require("../../../utils/encoding/toHex.js");
const hashMessage_js_1 = require("../../../utils/signature/hashMessage.js");
const hashTypedData_js_1 = require("../../../utils/signature/hashTypedData.js");
const parseSignature_js_1 = require("../../../utils/signature/parseSignature.js");
const abis_js_1 = require("../../constants/abis.js");
const address_js_1 = require("../../constants/address.js");
const getUserOperationHash_js_1 = require("../../utils/userOperation/getUserOperationHash.js");
const toSmartAccount_js_1 = require("../toSmartAccount.js");
const factoryAddress = {
    '1.1': '0xba5ed110efdba3d005bfc882d75358acbbb85842',
    '1': '0x0ba5ed0c6aa8c49038f819e587e2633c4a9f428a',
};
async function toCoinbaseSmartAccount(parameters) {
    const { client, ownerIndex = 0, owners, nonce = 0n, version = '1', } = parameters;
    let address = parameters.address;
    const entryPoint = {
        abi: abis_js_1.entryPoint06Abi,
        address: address_js_1.entryPoint06Address,
        version: '0.6',
    };
    const factory = {
        abi: factoryAbi,
        address: factoryAddress[version],
    };
    const owners_bytes = owners.map((owner) => {
        if (typeof owner === 'string')
            return (0, pad_js_1.pad)(owner);
        if (owner.type === 'webAuthn')
            return owner.publicKey;
        if (owner.type === 'local')
            return (0, pad_js_1.pad)(owner.address);
        throw new base_js_1.BaseError('invalid owner type');
    });
    const owner = (() => {
        const owner = owners[ownerIndex] ?? owners[0];
        if (typeof owner === 'string')
            return { address: owner, type: 'address' };
        return owner;
    })();
    return (0, toSmartAccount_js_1.toSmartAccount)({
        client,
        entryPoint,
        extend: { abi, factory },
        async decodeCalls(data) {
            const result = (0, decodeFunctionData_js_1.decodeFunctionData)({
                abi,
                data,
            });
            if (result.functionName === 'execute')
                return [
                    { to: result.args[0], value: result.args[1], data: result.args[2] },
                ];
            if (result.functionName === 'executeBatch')
                return result.args[0].map((arg) => ({
                    to: arg.target,
                    value: arg.value,
                    data: arg.data,
                }));
            throw new base_js_1.BaseError(`unable to decode calls for "${result.functionName}"`);
        },
        async encodeCalls(calls) {
            if (calls.length === 1)
                return (0, encodeFunctionData_js_1.encodeFunctionData)({
                    abi,
                    functionName: 'execute',
                    args: [calls[0].to, calls[0].value ?? 0n, calls[0].data ?? '0x'],
                });
            return (0, encodeFunctionData_js_1.encodeFunctionData)({
                abi,
                functionName: 'executeBatch',
                args: [
                    calls.map((call) => ({
                        data: call.data ?? '0x',
                        target: call.to,
                        value: call.value ?? 0n,
                    })),
                ],
            });
        },
        async getAddress() {
            address ??= await (0, readContract_js_1.readContract)(client, {
                ...factory,
                functionName: 'getAddress',
                args: [owners_bytes, nonce],
            });
            return address;
        },
        async getFactoryArgs() {
            const factoryData = (0, encodeFunctionData_js_1.encodeFunctionData)({
                abi: factory.abi,
                functionName: 'createAccount',
                args: [owners_bytes, nonce],
            });
            return { factory: factory.address, factoryData };
        },
        async getStubSignature() {
            if (owner.type === 'webAuthn')
                return '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000001949fc7c88032b9fcb5f6efc7a7b8c63668eae9871b765e23123bb473ff57aa831a7c0d9276168ebcc29f2875a0239cffdf2a9cd1c2007c5c77c071db9264df1d000000000000000000000000000000000000000000000000000000000000002549960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a7b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a2273496a396e6164474850596759334b7156384f7a4a666c726275504b474f716d59576f4d57516869467773222c226f726967696e223a2268747470733a2f2f7369676e2e636f696e626173652e636f6d222c2263726f73734f726967696e223a66616c73657d00000000000000000000000000000000000000000000';
            return wrapSignature({
                ownerIndex,
                signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c',
            });
        },
        async sign(parameters) {
            const address = await this.getAddress();
            const typedData = toReplaySafeTypedData({
                address,
                chainId: client.chain.id,
                hash: parameters.hash,
            });
            if (owner.type === 'address')
                throw new Error('owner cannot sign');
            const signature = await signTypedData({ owner, typedData });
            return wrapSignature({
                ownerIndex,
                signature,
            });
        },
        async signMessage(parameters) {
            const { message } = parameters;
            const address = await this.getAddress();
            const typedData = toReplaySafeTypedData({
                address,
                chainId: client.chain.id,
                hash: (0, hashMessage_js_1.hashMessage)(message),
            });
            if (owner.type === 'address')
                throw new Error('owner cannot sign');
            const signature = await signTypedData({ owner, typedData });
            return wrapSignature({
                ownerIndex,
                signature,
            });
        },
        async signTypedData(parameters) {
            const { domain, types, primaryType, message } = parameters;
            const address = await this.getAddress();
            const typedData = toReplaySafeTypedData({
                address,
                chainId: client.chain.id,
                hash: (0, hashTypedData_js_1.hashTypedData)({
                    domain,
                    message,
                    primaryType,
                    types,
                }),
            });
            if (owner.type === 'address')
                throw new Error('owner cannot sign');
            const signature = await signTypedData({ owner, typedData });
            return wrapSignature({
                ownerIndex,
                signature,
            });
        },
        async signUserOperation(parameters) {
            const { chainId = client.chain.id, ...userOperation } = parameters;
            const address = await this.getAddress();
            const hash = (0, getUserOperationHash_js_1.getUserOperationHash)({
                chainId,
                entryPointAddress: entryPoint.address,
                entryPointVersion: entryPoint.version,
                userOperation: {
                    ...userOperation,
                    sender: address,
                },
            });
            if (owner.type === 'address')
                throw new Error('owner cannot sign');
            const signature = await sign({ hash, owner });
            return wrapSignature({
                ownerIndex,
                signature,
            });
        },
        userOperation: {
            async estimateGas(userOperation) {
                if (owner.type !== 'webAuthn')
                    return;
                return {
                    verificationGasLimit: BigInt(Math.max(Number(userOperation.verificationGasLimit ?? 0n), 800_000)),
                };
            },
        },
    });
}
async function signTypedData({ typedData, owner, }) {
    if (owner.type === 'local' && owner.signTypedData)
        return owner.signTypedData(typedData);
    const hash = (0, hashTypedData_js_1.hashTypedData)(typedData);
    return sign({ hash, owner });
}
async function sign({ hash, owner, }) {
    if (owner.type === 'webAuthn') {
        const { signature, webauthn } = await owner.sign({
            hash,
        });
        return toWebAuthnSignature({ signature, webauthn });
    }
    if (owner.sign)
        return owner.sign({ hash });
    throw new base_js_1.BaseError('`owner` does not support raw sign.');
}
function toReplaySafeTypedData({ address, chainId, hash, }) {
    return {
        domain: {
            chainId,
            name: 'Coinbase Smart Wallet',
            verifyingContract: address,
            version: '1',
        },
        types: {
            CoinbaseSmartWalletMessage: [
                {
                    name: 'hash',
                    type: 'bytes32',
                },
            ],
        },
        primaryType: 'CoinbaseSmartWalletMessage',
        message: {
            hash,
        },
    };
}
function toWebAuthnSignature({ webauthn, signature, }) {
    const { r, s } = Signature.fromHex(signature);
    return (0, encodeAbiParameters_js_1.encodeAbiParameters)([
        {
            components: [
                {
                    name: 'authenticatorData',
                    type: 'bytes',
                },
                { name: 'clientDataJSON', type: 'bytes' },
                { name: 'challengeIndex', type: 'uint256' },
                { name: 'typeIndex', type: 'uint256' },
                {
                    name: 'r',
                    type: 'uint256',
                },
                {
                    name: 's',
                    type: 'uint256',
                },
            ],
            type: 'tuple',
        },
    ], [
        {
            authenticatorData: webauthn.authenticatorData,
            clientDataJSON: (0, toHex_js_1.stringToHex)(webauthn.clientDataJSON),
            challengeIndex: BigInt(webauthn.challengeIndex ?? 0n),
            typeIndex: BigInt(webauthn.typeIndex ?? 0n),
            r,
            s,
        },
    ]);
}
function wrapSignature(parameters) {
    const { ownerIndex = 0 } = parameters;
    const signatureData = (() => {
        if ((0, size_js_1.size)(parameters.signature) !== 65)
            return parameters.signature;
        const signature = (0, parseSignature_js_1.parseSignature)(parameters.signature);
        return (0, encodePacked_js_1.encodePacked)(['bytes32', 'bytes32', 'uint8'], [signature.r, signature.s, signature.yParity === 0 ? 27 : 28]);
    })();
    return (0, encodeAbiParameters_js_1.encodeAbiParameters)([
        {
            components: [
                {
                    name: 'ownerIndex',
                    type: 'uint8',
                },
                {
                    name: 'signatureData',
                    type: 'bytes',
                },
            ],
            type: 'tuple',
        },
    ], [
        {
            ownerIndex,
            signatureData,
        },
    ]);
}
const abi = [
    { inputs: [], stateMutability: 'nonpayable', type: 'constructor' },
    {
        inputs: [{ name: 'owner', type: 'bytes' }],
        name: 'AlreadyOwner',
        type: 'error',
    },
    { inputs: [], name: 'Initialized', type: 'error' },
    {
        inputs: [{ name: 'owner', type: 'bytes' }],
        name: 'InvalidEthereumAddressOwner',
        type: 'error',
    },
    {
        inputs: [{ name: 'key', type: 'uint256' }],
        name: 'InvalidNonceKey',
        type: 'error',
    },
    {
        inputs: [{ name: 'owner', type: 'bytes' }],
        name: 'InvalidOwnerBytesLength',
        type: 'error',
    },
    { inputs: [], name: 'LastOwner', type: 'error' },
    {
        inputs: [{ name: 'index', type: 'uint256' }],
        name: 'NoOwnerAtIndex',
        type: 'error',
    },
    {
        inputs: [{ name: 'ownersRemaining', type: 'uint256' }],
        name: 'NotLastOwner',
        type: 'error',
    },
    {
        inputs: [{ name: 'selector', type: 'bytes4' }],
        name: 'SelectorNotAllowed',
        type: 'error',
    },
    { inputs: [], name: 'Unauthorized', type: 'error' },
    { inputs: [], name: 'UnauthorizedCallContext', type: 'error' },
    { inputs: [], name: 'UpgradeFailed', type: 'error' },
    {
        inputs: [
            { name: 'index', type: 'uint256' },
            { name: 'expectedOwner', type: 'bytes' },
            { name: 'actualOwner', type: 'bytes' },
        ],
        name: 'WrongOwnerAtIndex',
        type: 'error',
    },
    {
        anonymous: false,
        inputs: [
            {
                indexed: true,
                name: 'index',
                type: 'uint256',
            },
            { indexed: false, name: 'owner', type: 'bytes' },
        ],
        name: 'AddOwner',
        type: 'event',
    },
    {
        anonymous: false,
        inputs: [
            {
                indexed: true,
                name: 'index',
                type: 'uint256',
            },
            { indexed: false, name: 'owner', type: 'bytes' },
        ],
        name: 'RemoveOwner',
        type: 'event',
    },
    {
        anonymous: false,
        inputs: [
            {
                indexed: true,
                name: 'implementation',
                type: 'address',
            },
        ],
        name: 'Upgraded',
        type: 'event',
    },
    { stateMutability: 'payable', type: 'fallback' },
    {
        inputs: [],
        name: 'REPLAYABLE_NONCE_KEY',
        outputs: [{ name: '', type: 'uint256' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [{ name: 'owner', type: 'address' }],
        name: 'addOwnerAddress',
        outputs: [],
        stateMutability: 'nonpayable',
        type: 'function',
    },
    {
        inputs: [
            { name: 'x', type: 'bytes32' },
            { name: 'y', type: 'bytes32' },
        ],
        name: 'addOwnerPublicKey',
        outputs: [],
        stateMutability: 'nonpayable',
        type: 'function',
    },
    {
        inputs: [{ name: 'functionSelector', type: 'bytes4' }],
        name: 'canSkipChainIdValidation',
        outputs: [{ name: '', type: 'bool' }],
        stateMutability: 'pure',
        type: 'function',
    },
    {
        inputs: [],
        name: 'domainSeparator',
        outputs: [{ name: '', type: 'bytes32' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [],
        name: 'eip712Domain',
        outputs: [
            { name: 'fields', type: 'bytes1' },
            { name: 'name', type: 'string' },
            { name: 'version', type: 'string' },
            { name: 'chainId', type: 'uint256' },
            { name: 'verifyingContract', type: 'address' },
            { name: 'salt', type: 'bytes32' },
            { name: 'extensions', type: 'uint256[]' },
        ],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [],
        name: 'entryPoint',
        outputs: [{ name: '', type: 'address' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [
            { name: 'target', type: 'address' },
            { name: 'value', type: 'uint256' },
            { name: 'data', type: 'bytes' },
        ],
        name: 'execute',
        outputs: [],
        stateMutability: 'payable',
        type: 'function',
    },
    {
        inputs: [
            {
                components: [
                    { name: 'target', type: 'address' },
                    { name: 'value', type: 'uint256' },
                    { name: 'data', type: 'bytes' },
                ],
                name: 'calls',
                type: 'tuple[]',
            },
        ],
        name: 'executeBatch',
        outputs: [],
        stateMutability: 'payable',
        type: 'function',
    },
    {
        inputs: [{ name: 'calls', type: 'bytes[]' }],
        name: 'executeWithoutChainIdValidation',
        outputs: [],
        stateMutability: 'payable',
        type: 'function',
    },
    {
        inputs: [
            {
                components: [
                    { name: 'sender', type: 'address' },
                    { name: 'nonce', type: 'uint256' },
                    { name: 'initCode', type: 'bytes' },
                    { name: 'callData', type: 'bytes' },
                    { name: 'callGasLimit', type: 'uint256' },
                    {
                        name: 'verificationGasLimit',
                        type: 'uint256',
                    },
                    {
                        name: 'preVerificationGas',
                        type: 'uint256',
                    },
                    { name: 'maxFeePerGas', type: 'uint256' },
                    {
                        name: 'maxPriorityFeePerGas',
                        type: 'uint256',
                    },
                    { name: 'paymasterAndData', type: 'bytes' },
                    { name: 'signature', type: 'bytes' },
                ],
                name: 'userOp',
                type: 'tuple',
            },
        ],
        name: 'getUserOpHashWithoutChainId',
        outputs: [{ name: '', type: 'bytes32' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [],
        name: 'implementation',
        outputs: [{ name: '$', type: 'address' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [{ name: 'owners', type: 'bytes[]' }],
        name: 'initialize',
        outputs: [],
        stateMutability: 'payable',
        type: 'function',
    },
    {
        inputs: [{ name: 'account', type: 'address' }],
        name: 'isOwnerAddress',
        outputs: [{ name: '', type: 'bool' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [{ name: 'account', type: 'bytes' }],
        name: 'isOwnerBytes',
        outputs: [{ name: '', type: 'bool' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [
            { name: 'x', type: 'bytes32' },
            { name: 'y', type: 'bytes32' },
        ],
        name: 'isOwnerPublicKey',
        outputs: [{ name: '', type: 'bool' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [
            { name: 'hash', type: 'bytes32' },
            { name: 'signature', type: 'bytes' },
        ],
        name: 'isValidSignature',
        outputs: [{ name: 'result', type: 'bytes4' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [],
        name: 'nextOwnerIndex',
        outputs: [{ name: '', type: 'uint256' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [{ name: 'index', type: 'uint256' }],
        name: 'ownerAtIndex',
        outputs: [{ name: '', type: 'bytes' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [],
        name: 'ownerCount',
        outputs: [{ name: '', type: 'uint256' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [],
        name: 'proxiableUUID',
        outputs: [{ name: '', type: 'bytes32' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [
            { name: 'index', type: 'uint256' },
            { name: 'owner', type: 'bytes' },
        ],
        name: 'removeLastOwner',
        outputs: [],
        stateMutability: 'nonpayable',
        type: 'function',
    },
    {
        inputs: [
            { name: 'index', type: 'uint256' },
            { name: 'owner', type: 'bytes' },
        ],
        name: 'removeOwnerAtIndex',
        outputs: [],
        stateMutability: 'nonpayable',
        type: 'function',
    },
    {
        inputs: [],
        name: 'removedOwnersCount',
        outputs: [{ name: '', type: 'uint256' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [{ name: 'hash', type: 'bytes32' }],
        name: 'replaySafeHash',
        outputs: [{ name: '', type: 'bytes32' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [
            { name: 'newImplementation', type: 'address' },
            { name: 'data', type: 'bytes' },
        ],
        name: 'upgradeToAndCall',
        outputs: [],
        stateMutability: 'payable',
        type: 'function',
    },
    {
        inputs: [
            {
                components: [
                    { name: 'sender', type: 'address' },
                    { name: 'nonce', type: 'uint256' },
                    { name: 'initCode', type: 'bytes' },
                    { name: 'callData', type: 'bytes' },
                    { name: 'callGasLimit', type: 'uint256' },
                    {
                        name: 'verificationGasLimit',
                        type: 'uint256',
                    },
                    {
                        name: 'preVerificationGas',
                        type: 'uint256',
                    },
                    { name: 'maxFeePerGas', type: 'uint256' },
                    {
                        name: 'maxPriorityFeePerGas',
                        type: 'uint256',
                    },
                    { name: 'paymasterAndData', type: 'bytes' },
                    { name: 'signature', type: 'bytes' },
                ],
                name: 'userOp',
                type: 'tuple',
            },
            { name: 'userOpHash', type: 'bytes32' },
            { name: 'missingAccountFunds', type: 'uint256' },
        ],
        name: 'validateUserOp',
        outputs: [{ name: 'validationData', type: 'uint256' }],
        stateMutability: 'nonpayable',
        type: 'function',
    },
    { stateMutability: 'payable', type: 'receive' },
];
const factoryAbi = [
    {
        inputs: [{ name: 'implementation_', type: 'address' }],
        stateMutability: 'payable',
        type: 'constructor',
    },
    { inputs: [], name: 'OwnerRequired', type: 'error' },
    {
        inputs: [
            { name: 'owners', type: 'bytes[]' },
            { name: 'nonce', type: 'uint256' },
        ],
        name: 'createAccount',
        outputs: [
            {
                name: 'account',
                type: 'address',
            },
        ],
        stateMutability: 'payable',
        type: 'function',
    },
    {
        inputs: [
            { name: 'owners', type: 'bytes[]' },
            { name: 'nonce', type: 'uint256' },
        ],
        name: 'getAddress',
        outputs: [{ name: '', type: 'address' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [],
        name: 'implementation',
        outputs: [{ name: '', type: 'address' }],
        stateMutability: 'view',
        type: 'function',
    },
    {
        inputs: [],
        name: 'initCodeHash',
        outputs: [{ name: '', type: 'bytes32' }],
        stateMutability: 'view',
        type: 'function',
    },
];
//# sourceMappingURL=toCoinbaseSmartAccount.js.map