diff --git a/README.md b/README.md index b1af3e9..4b91849 100644 --- a/README.md +++ b/README.md @@ -355,16 +355,16 @@ e.g.: `'[{"id":"cpu","amount":3},{"id":"ram","amount":16772672536},{"id":"disk", **Create Access List:** - **Positional:** - `npm run cli createAccessList "My Access List" "MAL" false "0xUser1,0xUser2"` + `npm run cli createAccessList "My Access List" "MAL" "0xUser1,0xUser2" false` - **Named Options:** - `npm run cli createAccessList --name "My Access List" --symbol "MAL" --transferable false --users "0xUser1,0xUser2"` + `npm run cli createAccessList --name "My Access List" --symbol "MAL" --initial-users "0xUser1,0xUser2" --transferable false` - Arguments: - `name`: Name for the access list - `symbol`: Symbol for the access list - - `transferable`: Whether tokens are transferable (true/false) - `initialUsers`: Comma-separated list of initial user addresses (optional) + - `transferable`: Whether tokens are transferable (true/false, optional, default: `false`) --- @@ -490,8 +490,8 @@ e.g.: `'[{"id":"cpu","amount":3},{"id":"ram","amount":16772672536},{"id":"disk", - **createAccessList:** `-n, --name ` `-s, --symbol ` - `-t, --transferable [transferable]` (Default: `false`) - `-u, --users [initialUsers]` (Default: `''`) + `-u, --initial-users [initialUsers]` (Default: `''`) + `-t, --transferable [transferable]` (Default: `false`) - **addToAccessList:** `-a, --address ` diff --git a/src/cli.ts b/src/cli.ts index f59eda1..c958ee8 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -680,36 +680,36 @@ export async function createCLI() { .description("Create a new access list contract") .argument("", "Name for the access list") .argument("", "Symbol for the access list") + .argument( + "[initialUsers]", + "Comma-separated list of initial user addresses", + "" + ) .argument( "[transferable]", "Whether tokens are transferable (true/false)", "false" ) - .argument( - "[initialUsers]", + .option("-n, --name ", "Name for the access list") + .option("-s, --symbol ", "Symbol for the access list") + .option( + "-u, --initial-users [initialUsers]", "Comma-separated list of initial user addresses", "" ) - .option("-n, --name ", "Name for the access list") - .option("-s, --symbol ", "Symbol for the access list") .option( "-t, --transferable [transferable]", "Whether tokens are transferable (true/false)", "false" ) - .option( - "-u, --users [initialUsers]", - "Comma-separated list of initial user addresses", - "" - ) - .action(async (name, symbol, transferable, initialUsers, options) => { + .action(async (name, symbol, initialUsers, transferable, options) => { const { signer, chainId } = await initializeSigner(); const commands = new Commands(signer, chainId); await commands.createAccessList([ options.name || name, options.symbol || symbol, options.transferable || transferable, - options.users || initialUsers, + options.initialUsers || initialUsers, ]); }); @@ -818,8 +818,8 @@ export async function createCLI() { program .command("createBucket") - .description("Create a new persistent-storage bucket gated by a single access list (chain inferred from RPC)") - .argument("", "Access list contract address (0x…)") + .description("Create a new persistent-storage bucket. Pass an access list to gate it; omit for owner-only access (chain inferred from RPC)") + .argument("[accessListAddress]", "Access list contract address (0x…); omit for owner-only access") .action(async (accessListAddress) => { const { signer, chainId } = await initializeSigner(); const commands = new Commands(signer, chainId); diff --git a/src/commands.ts b/src/commands.ts index fdeb47f..148b4e9 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -1799,17 +1799,17 @@ export class Commands { public async createBucket(args: string[]): Promise { try { const accessListAddress = args[1]; - if (!accessListAddress) { - console.error(chalk.red("accessListAddress is required")); - return; - } - if (!/^0x[a-fA-F0-9]{40}$/.test(accessListAddress)) { - console.error(chalk.red(`Invalid access list address: ${accessListAddress}`)); - return; + let accessLists: Array<{ [chainId: string]: string[] }> = []; + + if (accessListAddress) { + if (!/^0x[a-fA-F0-9]{40}$/.test(accessListAddress)) { + console.error(chalk.red(`Invalid access list address: ${accessListAddress}`)); + return; + } + const { chainId } = await this.signer.provider.getNetwork(); + accessLists = [{ [String(chainId)]: [accessListAddress] }]; } - const { chainId } = await this.signer.provider.getNetwork(); - const accessLists = [{ [String(chainId)]: [accessListAddress] }]; const result = await ProviderInstance.createPersistentStorageBucket( this.oceanNodeUrl, this.signer, diff --git a/test/accessList.test.ts b/test/accessList.test.ts index 39780d2..72f776a 100644 --- a/test/accessList.test.ts +++ b/test/accessList.test.ts @@ -33,10 +33,9 @@ describe("Ocean CLI Access List", function () { it("should create a new access list contract", async function () { const name = "TestAccessList"; const symbol = "TAL"; - const transferable = "false"; const output = await runCommand( - `npm run cli createAccessList ${name} ${symbol} ${transferable}` + `npm run cli createAccessList ${name} ${symbol}` ); expect(output).to.include("Access list created successfully"); @@ -58,7 +57,7 @@ describe("Ocean CLI Access List", function () { const initialUsers = `${testUser1.address},${testUser2.address}`; const output = await runCommand( - `npm run cli createAccessList ${name} ${symbol} ${transferable} ${initialUsers}` + `npm run cli createAccessList ${name} ${symbol} ${initialUsers} ${transferable}` ); expect(output).to.include("Access list created successfully"); @@ -238,7 +237,7 @@ describe("Ocean CLI Access List", function () { describe("Edge Cases", function () { it("should handle empty initial users list", async function () { const output = await runCommand( - `npm run cli createAccessList EmptyList EL false ""` + `npm run cli createAccessList EmptyList EL "" false` ); expect(output).to.include("Access list created successfully"); @@ -263,7 +262,7 @@ describe("Ocean CLI Access List", function () { describe("E2E Workflow", function () { it("should complete a full access list workflow", async function () { const createOutput = await runCommand( - `npm run cli createAccessList WorkflowTest WT false` + `npm run cli createAccessList WorkflowTest WT` ); expect(createOutput).to.include("Access list created successfully"); diff --git a/test/storage.test.ts b/test/storage.test.ts index f97a56d..d38a0dc 100644 --- a/test/storage.test.ts +++ b/test/storage.test.ts @@ -41,7 +41,7 @@ describe("Ocean CLI Persistent Storage", function () { it("should create an access list and add Alice and Bob", async function () { const createOutput = await runCommand( - `npm run cli createAccessList StorageTestACL STACL false` + `npm run cli createAccessList StorageTestACL STACL` ); expect(createOutput).to.include("Access list created successfully"); const addressMatch = createOutput.match( @@ -122,4 +122,42 @@ describe("Ocean CLI Persistent Storage", function () { ); expect(listOutput).to.not.include(fileName); }); + + describe("Owner-only bucket (no ACL)", function () { + let ownerOnlyBucketId: string; + + it("Alice should create a bucket without an access list", async function () { + const output = await runCommand(`npm run cli createBucket`); + expect(output).to.include("Bucket created."); + const idMatch = output.match( + /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/ + ); + if (!idMatch) { + throw new Error("Could not extract bucketId from output"); + } + ownerOnlyBucketId = idMatch[0]; + expect(output.toLowerCase()).to.include(alice.address.toLowerCase()); + }); + + it("Alice (owner) should upload and list files in the no-ACL bucket", async function () { + const uploadOutput = await runCommand( + `npm run cli addFileToBucket ${ownerOnlyBucketId} ${tempFilePath}` + ); + expect(uploadOutput).to.include(fileName); + + const listOutput = await runCommand( + `npm run cli listFilesInBucket ${ownerOnlyBucketId}` + ); + expect(listOutput).to.include(fileName); + expect(listOutput).to.not.match(/Error listing files/i); + }); + + it("Bob (not the owner) should not see Alice's files in the no-ACL bucket", async function () { + const bobOutput = await runCommandAs( + BOB_KEY, + `npm run cli listFilesInBucket ${ownerOnlyBucketId}` + ); + expect(bobOutput).to.not.include(fileName); + }); + }); });