Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
55 changes: 50 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,41 @@ 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(
new Error(
(error as Error).message
.replace("Failed to execute the IP Account transaction: ", "")
.trim(),
),
"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);
});
});
});