Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Optimize Smart Contract API input and add more tests #350

Merged
merged 1 commit into from
Dec 19, 2024
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
30 changes: 17 additions & 13 deletions src/coinbase/smart_contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import {
NFTContractOptions,
TokenContractOptions,
MultiTokenContractOptions,
RegisterContractOptions,
TransactionStatus,
PaginationOptions,
PaginationResponse,
UpdateContractOptions,
} from "./types";
import { Coinbase } from "./coinbase";
import { delay } from "./utils";
Expand Down Expand Up @@ -105,18 +107,19 @@ export class SmartContract {
/**
* Register a smart contract.
*
* @param networkId - The network ID.
* @param contractAddress - The contract address.
* @param abi - The ABI of the contract.
* @param contractName - The contract name.
* @param options - The options to register a smart contract.
* @param options.networkId - The network ID.
* @param options.contractAddress - The contract address.
* @param options.abi - The ABI of the contract.
* @param options.contractName - The contract name.
* @returns The smart contract.
*/
public static async register(
networkId: string,
contractAddress: string,
abi: object,
contractName?: string,
): Promise<SmartContract> {
public static async register({
networkId,
contractAddress,
abi,
contractName,
}: RegisterContractOptions): Promise<SmartContract> {
const response = await Coinbase.apiClients.smartContract!.registerSmartContract(
networkId,
contractAddress,
Expand Down Expand Up @@ -317,11 +320,12 @@ export class SmartContract {
/**
* Update a smart contract.
*
* @param abi - The new ABI of the contract.
* @param contractName - The new contract name.
* @param options - The options to update a smart contract.
* @param options.abi - The new ABI of the contract.
* @param options.contractName - The new contract name.
* @returns The smart contract.
*/
public async update(abi?: object, contractName?: string): Promise<SmartContract> {
public async update({ abi, contractName }: UpdateContractOptions): Promise<SmartContract> {
const response = await Coinbase.apiClients.smartContract!.updateSmartContract(
this.getNetworkId(),
this.getContractAddress(),
Expand Down
18 changes: 18 additions & 0 deletions src/coinbase/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,24 @@ export type UpdateWebhookOptions = {
eventTypeFilter?: WebhookEventTypeFilter;
};

/**
* Options for registering a smart contract.
*/
export type RegisterContractOptions = {
networkId: string;
contractAddress: string;
abi: object;
contractName?: string;
};

/**
* Options for updating a smart contract.
*/
export type UpdateContractOptions = {
abi?: object;
contractName?: string;
};

/**
* ContractInvocationAPI client type definition.
*/
Expand Down
124 changes: 103 additions & 21 deletions src/tests/smart_contract_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ describe("SmartContract", () => {
.fn()
.mockResolvedValue({ data: erc20ExternalModel });

const smartContract = await SmartContract.register(
networkId,
contractAddress,
testAllReadTypesABI,
contractName,
);
const smartContract = await SmartContract.register({
networkId: networkId,
contractAddress: contractAddress,
abi: testAllReadTypesABI,
contractName: contractName,
});

expect(Coinbase.apiClients.smartContract!.registerSmartContract).toHaveBeenCalledWith(
networkId,
Expand All @@ -94,7 +94,12 @@ describe("SmartContract", () => {
.fn()
.mockRejectedValue(new Error("Failed to register the smart contract"));
await expect(
SmartContract.register(networkId, contractAddress, testAllReadTypesABI, contractName),
SmartContract.register({
networkId: networkId,
contractAddress: contractAddress,
abi: testAllReadTypesABI,
contractName: contractName,
}),
).rejects.toThrow("Failed to register the smart contract");
});
});
Expand All @@ -103,21 +108,21 @@ describe("SmartContract", () => {
const networkId = erc20ExternalModel.network_id;
const contractAddress = erc20ExternalModel.contract_address;

const updatedContract = JSON.parse(JSON.stringify(erc20ExternalModel));
const updatedAbiJson = { abi: "data2" };
updatedContract.contract_name = "UpdatedContractName";
updatedContract.abi = JSON.stringify(updatedAbiJson);

it("should update an existing smart contract", async () => {
const updatedContract = JSON.parse(JSON.stringify(erc20ExternalModel));
const updatedAbiJson = { abi: "data2" };
updatedContract.contract_name = "UpdatedContractName";
updatedContract.abi = JSON.stringify(updatedAbiJson);

Coinbase.apiClients.smartContract = smartContractApiMock;
Coinbase.apiClients.smartContract.updateSmartContract = jest
.fn()
.mockResolvedValue({ data: updatedContract });

const smartContract = await erc20ExternalSmartContract.update(
updatedAbiJson,
updatedContract.contract_name,
);
const smartContract = await erc20ExternalSmartContract.update({
abi: updatedAbiJson,
contractName: updatedContract.contract_name,
});

expect(Coinbase.apiClients.smartContract!.updateSmartContract).toHaveBeenCalledWith(
networkId,
Expand All @@ -133,12 +138,87 @@ describe("SmartContract", () => {
expect(smartContract.getContractName()).toEqual(updatedContract.contract_name);
});

it("should update an existing smart contract - update contract name only", async () => {
const updatedContract = JSON.parse(JSON.stringify(erc20ExternalModel));
updatedContract.contract_name = "UpdatedContractName";

Coinbase.apiClients.smartContract = smartContractApiMock;
Coinbase.apiClients.smartContract.updateSmartContract = jest
.fn()
.mockResolvedValue({ data: updatedContract });

const smartContract = await erc20ExternalSmartContract.update({
contractName: updatedContract.contract_name,
});

expect(Coinbase.apiClients.smartContract!.updateSmartContract).toHaveBeenCalledWith(
networkId,
contractAddress,
{
contract_name: updatedContract.contract_name,
abi: undefined,
},
);
expect(smartContract).toBeInstanceOf(SmartContract);
expect(smartContract.getContractAddress()).toBe(contractAddress);
expect(smartContract.getAbi()).toEqual(erc20ExternalSmartContract.getAbi());
expect(smartContract.getContractName()).toEqual(updatedContract.contract_name);
});

it("should update an existing smart contract - update abi only", async () => {
const updatedContract = JSON.parse(JSON.stringify(erc20ExternalModel));
const updatedAbiJson = { abi: "data2" };
updatedContract.abi = JSON.stringify(updatedAbiJson);

Coinbase.apiClients.smartContract = smartContractApiMock;
Coinbase.apiClients.smartContract.updateSmartContract = jest
.fn()
.mockResolvedValue({ data: updatedContract });

const smartContract = await erc20ExternalSmartContract.update({ abi: updatedAbiJson });

expect(Coinbase.apiClients.smartContract!.updateSmartContract).toHaveBeenCalledWith(
networkId,
contractAddress,
{
contract_name: undefined,
abi: updatedContract.abi,
},
);
expect(smartContract).toBeInstanceOf(SmartContract);
expect(smartContract.getContractAddress()).toBe(contractAddress);
expect(smartContract.getAbi()).toEqual(updatedAbiJson);
expect(smartContract.getContractName()).toEqual(erc20ExternalSmartContract.getContractName());
});

it("should update an existing smart contract - no update", async () => {
Coinbase.apiClients.smartContract = smartContractApiMock;
Coinbase.apiClients.smartContract.updateSmartContract = jest
.fn()
.mockResolvedValue({ data: erc20ExternalModel });

const smartContract = await erc20ExternalSmartContract.update({});

expect(Coinbase.apiClients.smartContract!.updateSmartContract).toHaveBeenCalledWith(
networkId,
contractAddress,
{},
);
expect(smartContract).toBeInstanceOf(SmartContract);
expect(smartContract.getContractAddress()).toBe(contractAddress);
expect(smartContract.getAbi()).toEqual(erc20ExternalSmartContract.getAbi());
expect(smartContract.getContractName()).toEqual(erc20ExternalSmartContract.getContractName());
});

it("should throw an error if update fails", async () => {
Coinbase.apiClients.smartContract!.updateSmartContract = jest
.fn()
.mockRejectedValue(new Error("Failed to update the smart contract"));
await expect(
erc20ExternalSmartContract.update(testAllReadTypesABI, updatedContract.contract_name),
erc20ExternalSmartContract.update({
abi: testAllReadTypesABI,
contractName: erc20ExternalSmartContract.getContractName(),
}),
).rejects.toThrow("Failed to update the smart contract");
});
});
Expand Down Expand Up @@ -195,7 +275,7 @@ describe("SmartContract", () => {
});
});

describe('#getWalletId', () => {
describe("#getWalletId", () => {
it("returns the smart contract wallet ID", () => {
expect(erc20SmartContract.getWalletId()).toEqual(VALID_SMART_CONTRACT_ERC20_MODEL.wallet_id);
});
Expand Down Expand Up @@ -474,16 +554,18 @@ describe("SmartContract", () => {
});

it("throws an error when the smart contract is external", async () => {
expect(externalSmartContract.reload()).rejects.toThrow("Cannot reload an external SmartContract");
expect(externalSmartContract.reload()).rejects.toThrow(
"Cannot reload an external SmartContract",
);
});
});

describe("#toString", () => {
it("returns the same value as toString", () => {
expect(erc20SmartContract.toString()).toEqual(
`SmartContract{id: '${erc20SmartContract.getId()}', networkId: '${erc20SmartContract.getNetworkId()}', ` +
`contractAddress: '${erc20SmartContract.getContractAddress()}', deployerAddress: '${erc20SmartContract.getDeployerAddress()}', ` +
`type: '${erc20SmartContract.getType()}'}`,
`contractAddress: '${erc20SmartContract.getContractAddress()}', deployerAddress: '${erc20SmartContract.getDeployerAddress()}', ` +
`type: '${erc20SmartContract.getType()}'}`,
);
});
});
Expand Down
Loading