Skip to content
Merged
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
2 changes: 1 addition & 1 deletion packages/core-sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ export class StoryClient {
*/
public get ipAccount(): IPAccountClient {
if (this._ipAccount === null) {
this._ipAccount = new IPAccountClient(this.rpcClient, this.wallet);
this._ipAccount = new IPAccountClient(this.rpcClient, this.wallet, this.chainId);
}

return this._ipAccount;
Expand Down
48 changes: 43 additions & 5 deletions packages/core-sdk/src/resources/ipAccount.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
import { Address, PublicClient } from "viem";
import { Address, Hex, encodeFunctionData, PublicClient } from "viem";

import {
IPAccountExecuteRequest,
IPAccountExecuteResponse,
IPAccountExecuteWithSigRequest,
IPAccountExecuteWithSigResponse,
IpAccountStateResponse,
SetIpMetadataRequest,
TokenResponse,
} from "../types/resources/ipAccount";
import { handleError } from "../utils/errors";
import { IpAccountImplClient, SimpleWalletClient } from "../abi/generated";
import { getAddress } from "../utils/utils";
import {
coreMetadataModuleAbi,
coreMetadataModuleAddress,
IpAccountImplClient,
SimpleWalletClient,
} from "../abi/generated";
import { getAddress, validateAddress } from "../utils/utils";
import { ChainIds } from "../types/config";

export class IPAccountClient {
private readonly wallet: SimpleWalletClient;
private readonly rpcClient: PublicClient;

constructor(rpcClient: PublicClient, wallet: SimpleWalletClient) {
private readonly chainId: ChainIds;
constructor(rpcClient: PublicClient, wallet: SimpleWalletClient, chainId: ChainIds) {
this.wallet = wallet;
this.rpcClient = rpcClient;
this.chainId = chainId;
}

/** Executes a transaction from the IP Account.
Expand Down Expand Up @@ -150,4 +158,34 @@ export class IPAccountClient {
handleError(error, "Failed to get the token");
}
}
/**
* Sets the metadataURI for an IP asset.
*/
public async setIpMetadata({
ipId,
metadataURI,
metadataHash,
txOptions,
}: SetIpMetadataRequest): Promise<Hex> {
try {
const data = encodeFunctionData({
abi: coreMetadataModuleAbi,
functionName: "setMetadataURI",
args: [validateAddress(ipId), metadataURI, metadataHash],
});
const { txHash } = await this.execute({
ipId: ipId,
to: coreMetadataModuleAddress[this.chainId],
data: data,
value: 0,
txOptions: {
...txOptions,
encodedTxDataOnly: false,
},
});
return txHash!;
} catch (error) {
handleError(error, "Failed to set the IP metadata");
}
}
}
3 changes: 0 additions & 3 deletions packages/core-sdk/src/resources/royalty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import {
SimpleWalletClient,
WrappedIpClient,
} from "../abi/generated";
import { IPAccountClient } from "./ipAccount";
import { getAddress, validateAddress, validateAddresses } from "../utils/utils";
import { WIP_TOKEN_ADDRESS } from "../constants/common";
import { contractCallWithFees } from "../utils/feeUtils";
Expand All @@ -43,7 +42,6 @@ import { simulateAndWriteContract } from "../utils/contract";
export class RoyaltyClient {
public royaltyModuleClient: RoyaltyModuleClient;
public ipAssetRegistryClient: IpAssetRegistryClient;
public ipAccountClient: IPAccountClient;
public ipRoyaltyVaultImplReadOnlyClient: IpRoyaltyVaultImplReadOnlyClient;
public ipRoyaltyVaultImplEventClient: IpRoyaltyVaultImplEventClient;
public multicall3Client: Multicall3Client;
Expand All @@ -57,7 +55,6 @@ export class RoyaltyClient {
this.ipAssetRegistryClient = new IpAssetRegistryClient(rpcClient, wallet);
this.ipRoyaltyVaultImplReadOnlyClient = new IpRoyaltyVaultImplReadOnlyClient(rpcClient);
this.ipRoyaltyVaultImplEventClient = new IpRoyaltyVaultImplEventClient(rpcClient);
this.ipAccountClient = new IPAccountClient(rpcClient, wallet);
this.multicall3Client = new Multicall3Client(rpcClient, wallet);
this.wrappedIpClient = new WrappedIpClient(rpcClient, wallet);
this.rpcClient = rpcClient;
Expand Down
13 changes: 11 additions & 2 deletions packages/core-sdk/src/types/resources/ipAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type IPAccountExecuteRequest = {
};

export type IPAccountExecuteResponse = {
txHash?: string;
txHash?: Hex;
encodedTxData?: EncodedTxData;
};

Expand All @@ -28,7 +28,7 @@ export type IPAccountExecuteWithSigRequest = {
};

export type IPAccountExecuteWithSigResponse = {
txHash?: string;
txHash?: Hex;
encodedTxData?: EncodedTxData;
};

Expand All @@ -39,3 +39,12 @@ export type TokenResponse = {
tokenContract: Address;
tokenId: bigint;
};

export type SetIpMetadataRequest = {
ipId: Address;
/** The metadataURI to set for the IP asset. */
metadataURI: string;
/** The hash of metadata at metadataURI. */
metadataHash: Hex;
txOptions?: Omit<TxOptions, "encodedTxDataOnly">;
};
11 changes: 10 additions & 1 deletion packages/core-sdk/test/integration/ipAccount.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import chai from "chai";
import chaiAsPromised from "chai-as-promised";
import { AccessPermission, StoryClient } from "../../src";
import { mockERC721, getStoryClient, getTokenId, aeneid } from "./utils/util";
import { Hex, encodeFunctionData, getAddress, toFunctionSelector } from "viem";
import { Hex, encodeFunctionData, getAddress, toFunctionSelector, toHex } from "viem";
import {
accessControllerAbi,
accessControllerAddress,
Expand Down Expand Up @@ -150,4 +150,13 @@ describe("IPAccount Functions", () => {
.rejected;
});
});

it("should successfully set ip metadata", async () => {
const txHash = await client.ipAccount.setIpMetadata({
ipId: ipId,
metadataURI: "https://example.com",
metadataHash: toHex("test", { size: 32 }),
});
expect(txHash).to.be.a("string").and.not.empty;
});
});
2 changes: 1 addition & 1 deletion packages/core-sdk/test/unit/mockData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const txHash = "0x063834efe214f4199b1ad7181ce8c5ced3e15d271c8e866da7c89e86ee629cfb";
export const ipId = "0x73fcb515cee99e4991465ef586cfe2b072ebb512";
export const aeneid = 13_15;
export const aeneid = "1315";
export const mockERC20 = "0x73fcb515cee99e4991465ef586cfe2b072ebb512";
export const walletAddress = "0x73fcb515cee99e4991465ef586cfe2b072ebb512";
30 changes: 26 additions & 4 deletions packages/core-sdk/test/unit/resources/ipAccount.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ import * as sinon from "sinon";
import { IPAccountClient } from "../../../src/resources/ipAccount";
import { IPAccountExecuteRequest, IPAccountExecuteWithSigRequest } from "../../../src";
import * as utils from "../../../src/utils/utils";
import { Account, PublicClient, WalletClient, zeroAddress } from "viem";
const { IpAccountImplClient } = require("../../../src/abi/generated");
import { Account, PublicClient, toHex, WalletClient, zeroAddress } from "viem";
import { aeneid, ipId, txHash } from "../mockData";
import { IpAccountImplClient } from "../../../src/abi/generated";

describe("Test IPAccountClient", () => {
let ipAccountClient: IPAccountClient;
let rpcMock: PublicClient;
let walletMock: WalletClient;
const txHash = "0x129f7dd802200f096221dd89d5b086e4bd3ad6eafb378a0c75e3b04fc375f997";
beforeEach(() => {
rpcMock = createMock<PublicClient>();
walletMock = createMock<WalletClient>();
const accountMock = createMock<Account>();
walletMock.account = accountMock;
ipAccountClient = new IPAccountClient(rpcMock, walletMock);
ipAccountClient = new IPAccountClient(rpcMock, walletMock, aeneid);
sinon.stub(IpAccountImplClient.prototype, "execute").resolves(txHash);
sinon.stub(IpAccountImplClient.prototype, "executeEncode").returns({ data: "0x", to: "0x" });
sinon.stub(IpAccountImplClient.prototype, "executeWithSig").resolves(txHash);
Expand Down Expand Up @@ -195,4 +195,26 @@ describe("Test IPAccountClient", () => {
expect(token).to.deep.equal({ chainId: 1513n, tokenContract: zeroAddress, tokenId: 1n });
});
});

describe("Test setIpMetadata", () => {
it("should throw error when call setIpMetadata given wrong ipId", async () => {
try {
await ipAccountClient.setIpMetadata({
ipId: "0x",
metadataURI: "https://example.com",
metadataHash: toHex("test", { size: 32 }),
});
} catch (err) {
expect((err as Error).message).equal("Failed to set the IP metadata: Invalid address: 0x.");
}
});
it("should return txHash when call setIpMetadata successfully", async () => {
const result = await ipAccountClient.setIpMetadata({
ipId: ipId,
metadataURI: "https://example.com",
metadataHash: toHex("test", { size: 32 }),
});
expect(result).to.equal(txHash);
});
});
});