UNPKG

kamiweb3-sdk

Version:

TypeScript SDK for KAMI721-C, KAMI721-AC, and KAMI1155-C smart contracts

435 lines 19.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ERC721CWrapper = void 0; const ethers_1 = require("ethers"); const types_1 = require("../types"); const KAMI721C_json_1 = __importDefault(require("../abis/KAMI721C.json")); const ethers = __importStar(require("ethers")); /** * Wraps an instance of the ERC721C contract (standard or upgradeable proxy) to provide typed methods. */ class ERC721CWrapper { /** * Creates an instance of ERC721CWrapper. * @param address The address of the standard contract or the proxy contract. * @param signerOrProvider A Signer (for transactions) or Provider (for read-only). * @param contractAbi (Optional) The ABI to use. Defaults to the standard KAMI721C ABI. Provide the KAMI721CUpgradeable ABI when attaching to a proxy. */ constructor(address, signerOrProvider, contractAbi) { // Use provided ABI or default to standard KAMI721C ABI this.abi = contractAbi || KAMI721C_json_1.default.abi; if (!this.abi || this.abi.length === 0) { // Check if the default was attempted and failed if (!contractAbi && (!KAMI721C_json_1.default || !KAMI721C_json_1.default.abi)) { throw new Error('Default KAMI721C ABI not found or invalid.'); } else { throw new Error('Provided ABI is invalid or ABI not found.'); } } this.contract = new ethers_1.Contract(address.toString(), this.abi, signerOrProvider); this.address = address.toString(); } // === Core ERC721 Functions === /** * Returns the number of tokens in `owner`'s account. * @throws {Error} If owner is the zero address. */ async balanceOf(owner) { if (owner === ethers_1.ZeroAddress) throw new Error('ERC721: balance query for the zero address'); return this.contract.getFunction('balanceOf')(owner); } /** * Returns the owner of the `tokenId` token. * @throws {Error} If the token does not exist. */ async ownerOf(tokenId) { return this.contract.getFunction('ownerOf')(tokenId); } /** * Safely transfers `tokenId` token from `from` to `to`. * @throws {Error} If caller is not owner nor approved, or if `to` is zero address. */ async safeTransferFrom(from, to, tokenId, data = '0x', overrides = {}) { this.requireSigner(); if (to === ethers_1.ZeroAddress) throw new Error('ERC721: transfer to the zero address'); // Ensure correct overload is called (based on presence/absence of data argument) if (data && data !== '0x' && ethers.getBytes(data).length > 0) { return this.contract.getFunction('safeTransferFrom(address,address,uint256,bytes)')(from, to, tokenId, data, overrides); } else { return this.contract.getFunction('safeTransferFrom(address,address,uint256)')(from, to, tokenId, overrides); } } /** * Transfers `tokenId` token from `from` to `to`. * Note: Usage of this method is discouraged, use `safeTransferFrom` whenever possible. * @throws {Error} If caller is not owner nor approved, or if `to` is zero address. */ async transferFrom(from, to, tokenId, overrides = {}) { this.requireSigner(); if (to === ethers_1.ZeroAddress) throw new Error('ERC721: transfer to the zero address'); return this.contract.getFunction('transferFrom')(from, to, tokenId, overrides); } /** * Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * @throws {Error} If `to` is the zero address. */ async approve(to, tokenId, overrides = {}) { this.requireSigner(); if (to === ethers_1.ZeroAddress) throw new Error('ERC721: approve to the zero address'); return this.contract.getFunction('approve')(to, tokenId, overrides); } /** * Returns the account approved for `tokenId` token. * @throws {Error} If the token does not exist. */ async getApproved(tokenId) { return this.contract.getFunction('getApproved')(tokenId); } /** * Approve or remove `operator` as an operator for the caller. */ async setApprovalForAll(operator, approved, overrides = {}) { this.requireSigner(); return this.contract.getFunction('setApprovalForAll')(operator, approved, overrides); } /** * Returns if the `operator` is allowed to manage all of the assets of `owner`. */ async isApprovedForAll(owner, operator) { return this.contract.getFunction('isApprovedForAll')(owner, operator); } /** * Returns the token collection name. */ async name() { return this.contract.getFunction('name')(); } /** * Returns the token collection symbol. */ async symbol() { return this.contract.getFunction('symbol')(); } /** * Returns the Uniform Resource Identifier (URI) for `tokenId` token. * @throws {Error} If the token does not exist. */ async tokenURI(tokenId) { return this.contract.getFunction('tokenURI')(tokenId); } // === ERC721Enumerable Functions === /** * Returns the total amount of tokens stored by the contract. */ async totalSupply() { return this.contract.getFunction('totalSupply')(); } /** * Returns a token ID owned by `owner` at a given `index` of its token list. * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. * @throws {Error} If `index` >= `balanceOf(owner)` or `owner` is zero address. */ async tokenOfOwnerByIndex(owner, index) { if (owner === ethers_1.ZeroAddress) throw new Error('Enumerable: owner index query for the zero address'); return this.contract.getFunction('tokenOfOwnerByIndex')(owner, index); } /** * Returns a token ID at a given `index` of all the tokens stored by the contract. * Use along with {totalSupply} to enumerate all tokens. * @throws {Error} If `index` >= `totalSupply()`. */ async tokenByIndex(index) { return this.contract.getFunction('tokenByIndex')(index); } // === ERC2981 Royalty Standard === /** * Returns the royalty information for a given token ID and sale price. * @throws {Error} If token does not exist. */ async royaltyInfo(tokenId, salePrice) { const [receiver, royaltyAmount] = await this.contract.getFunction('royaltyInfo')(tokenId, salePrice); return { receiver, royaltyAmount }; } // === KAMI Core Functions === /** * Mints a single new NFT to the caller's address. * Requires the sender to have approved the contract for the USDC mint cost. * @param overrides Optional transaction overrides. * @returns A promise that resolves to the transaction response. */ async mint(overrides = {}) { this.requireSigner(); // Mint function takes no arguments in KAMI721C ABI return this.contract.getFunction('mint')(...[], overrides); } /** * Sells an NFT from the owner (or approved operator) to a buyer. * Requires seller approval and buyer USDC allowance. * @throws {Error} If `to` is the zero address. * @param to The address of the buyer. * @param tokenId The ID of the token being sold. * @param salePrice The price in the smallest unit of USDC. * @param overrides Optional transaction overrides. * @returns A promise that resolves to the transaction response. */ async sellToken(to, tokenId, salePrice, overrides = {}) { this.requireSigner(); if (to === ethers_1.ZeroAddress) throw new Error('Cannot sell token to the zero address'); return this.contract.getFunction('sellToken')(to, tokenId, salePrice, overrides); } // === KAMI Rental Functions === /** Rents a token for a specified duration. Requires renter USDC allowance. */ async rentToken(tokenId, duration, rentalPrice, overrides = {}) { this.requireSigner(); return this.contract.getFunction('rentToken')(tokenId, duration, rentalPrice, overrides); } /** Ends a rental period early. Callable by owner or renter (verify contract logic). */ async endRental(tokenId, overrides = {}) { this.requireSigner(); return this.contract.getFunction('endRental')(tokenId, overrides); } /** Extends an existing rental. Requires renter USDC allowance for additional payment. */ async extendRental(tokenId, additionalDuration, additionalPayment, overrides = {}) { this.requireSigner(); return this.contract.getFunction('extendRental')(tokenId, additionalDuration, additionalPayment, overrides); } /** Gets the rental details for a specific token ID. */ async getRentalDetails(tokenId) { const result = await this.contract.getFunction('getRentalInfo')(tokenId); return { renter: result[0], rentalEndTime: result[2], }; } // === KAMI Royalty Management === /** Sets default mint royalties. Requires OWNER_ROLE. */ async setMintRoyalties(royalties, overrides = {}) { this.requireSigner(); await this.requireRole(types_1.OWNER_ROLE); return this.contract.getFunction('setMintRoyalties')(royalties, overrides); } /** Sets default transfer royalties. Requires OWNER_ROLE. */ async setTransferRoyalties(royalties, overrides = {}) { this.requireSigner(); await this.requireRole(types_1.OWNER_ROLE); return this.contract.getFunction('setTransferRoyalties')(royalties, overrides); } /** Sets token-specific mint royalties. Requires OWNER_ROLE. */ async setTokenMintRoyalties(tokenId, royalties, overrides = {}) { this.requireSigner(); await this.requireRole(types_1.OWNER_ROLE); return this.contract.getFunction('setTokenMintRoyalties')(tokenId, royalties, overrides); } /** Sets token-specific transfer royalties. Requires OWNER_ROLE. */ async setTokenTransferRoyalties(tokenId, royalties, overrides = {}) { this.requireSigner(); await this.requireRole(types_1.OWNER_ROLE); return this.contract.getFunction('setTokenTransferRoyalties')(tokenId, royalties, overrides); } /** Gets mint royalty receivers for a token. */ async getMintRoyaltyReceivers(tokenId) { return this.contract.getFunction('getMintRoyaltyReceivers')(tokenId); } /** * Gets the transfer royalty configuration for a specific token ID. */ async getTransferRoyaltyReceivers(tokenId) { let royaltiesRaw = await this.contract.getFunction('getTransferRoyaltyReceivers')(tokenId); // Ethers v6 sometimes wraps array results in an extra array layer if (Array.isArray(royaltiesRaw) && royaltiesRaw.length === 1 && Array.isArray(royaltiesRaw[0])) { royaltiesRaw = royaltiesRaw[0]; } // Check if it's an array of tuples/arrays if (!Array.isArray(royaltiesRaw)) { console.warn('getTransferRoyaltyReceivers result structure unexpected (not an array):', royaltiesRaw); return []; } // Map the array of tuples to an array of RoyaltyData objects return royaltiesRaw.map((royaltyTuple) => { // Basic validation of the tuple structure if (!Array.isArray(royaltyTuple) || royaltyTuple.length < 2) { console.warn('Unexpected royalty tuple structure:', royaltyTuple); // Return a default/empty object or throw, depending on desired handling return { receiver: ethers.ZeroAddress, feeNumerator: 0n }; } try { return { receiver: (0, ethers_1.getAddress)(royaltyTuple[0]), // Normalize address feeNumerator: BigInt(royaltyTuple[1]), // Ensure fee is BigInt }; } catch (e) { console.error('Error processing royalty tuple:', royaltyTuple, e); return { receiver: ethers.ZeroAddress, feeNumerator: 0n }; } }); } // === KAMI Configuration === /** Sets the mint price. Requires OWNER_ROLE. */ async setMintPrice(newMintPrice, overrides = {}) { this.requireSigner(); await this.requireRole(types_1.OWNER_ROLE); return this.contract.getFunction('setMintPrice')(newMintPrice, overrides); } /** Gets the current mint price. */ async getMintPrice() { return this.contract.getFunction('mintPrice')(); } /** Sets the platform commission percentage and address. Requires OWNER_ROLE. */ async setPlatformCommission(newPercentage, newPlatformAddress, overrides = {}) { this.requireSigner(); await this.requireRole(types_1.OWNER_ROLE); if (newPlatformAddress === ethers_1.ZeroAddress) throw new Error('Platform address cannot be the zero address'); return this.contract.getFunction('setPlatformCommission')(newPercentage, newPlatformAddress, overrides); } /** Gets the current platform address for commissions. */ async getPlatformAddress() { return this.contract.getFunction('platformAddress')(); } /** Gets the current platform commission percentage (basis points). */ async getPlatformCommissionPercentage() { return this.contract.getFunction('platformCommissionPercentage')(); } /** Sets the base URI for all token IDs. Requires OWNER_ROLE. */ async setBaseURI(baseURI, overrides = {}) { this.requireSigner(); return this.contract.getFunction('setBaseURI')(baseURI, overrides); } /** * Gets the base URI. */ async getBaseURI() { return this.contract.getFunction('baseURI')(); } /** Gets the address of the USDC contract used for payments. */ async usdc() { return this.contract.getFunction('usdc')(); } // === KAMI Administrative === /** Pauses the contract. Requires PAUSER_ROLE. */ async pause(overrides = {}) { this.requireSigner(); await this.requireRole(types_1.PAUSER_ROLE); return this.contract.getFunction('pause')(...[], overrides); } /** Returns true if the contract is paused. */ async paused() { return this.contract.getFunction('paused')(); } /** Unpauses the contract. Requires PAUSER_ROLE. */ async unpause(overrides = {}) { this.requireSigner(); await this.requireRole(types_1.PAUSER_ROLE); return this.contract.getFunction('unpause')(...[], overrides); } /** Burns (destroys) a specific token. Requires caller to be owner or approved. */ async burn(tokenId, overrides = {}) { this.requireSigner(); // Requires token owner or approved operator - contract handles this check return this.contract.getFunction('burn')(tokenId, overrides); } // === Access Control === /** Checks if an account has a specific role. */ async hasRole(role, account) { return this.contract.getFunction('hasRole')(role, account); } /** Gets the admin role for a specific role. */ async getRoleAdmin(role) { return this.contract.getFunction('getRoleAdmin')(role); } /** Grants a role to an account. Requires caller to have the admin role for the role being granted. */ async grantRole(role, account, overrides = {}) { this.requireSigner(); const adminRole = await this.getRoleAdmin(role); await this.requireRole(adminRole, `Caller must have admin role (${adminRole}) to grant role ${role}`); return this.contract.getFunction('grantRole')(role, account, overrides); } /** Revokes a role from an account. Requires caller to have the admin role for the role being revoked. */ async revokeRole(role, account, overrides = {}) { this.requireSigner(); const adminRole = await this.getRoleAdmin(role); await this.requireRole(adminRole, `Caller must have admin role (${adminRole}) to revoke role ${role}`); return this.contract.getFunction('revokeRole')(role, account, overrides); } /** Renounces a role for the caller's own account. */ async renounceRole(role, overrides = {}) { // Note: The ABI has renounceRole(bytes32 role, address account), // but OZ's standard implementation prevents renouncing for others. // We get the caller's address to pass as the account argument. const signer = this.requireSigner(); const callerAddress = await signer.getAddress(); return this.contract.getFunction('renounceRole')(role, callerAddress, overrides); } // === Helper Methods === requireSigner() { const runner = this.contract.runner; if (!runner || runner.provider === null) { throw new Error('A signer is required for this operation.'); } return runner; } async requireRole(role, errorMessage) { const signer = this.requireSigner(); const address = await signer.getAddress(); if (!(await this.hasRole(role, address))) { throw new Error(errorMessage || `Caller does not have required role: ${role}`); } } /** * Connects a different signer or provider to the contract wrapper. * Preserves the ABI used when the original wrapper was created. * @param signerOrProvider The new signer or provider. * @returns A new ERC721CWrapper instance connected with the new signer/provider. */ connect(signerOrProvider) { return new ERC721CWrapper(this.address, signerOrProvider, this.abi); } } exports.ERC721CWrapper = ERC721CWrapper; //# sourceMappingURL=ERC721CWrapper.js.map