Skip to content

Commit a445655

Browse files
authored
Access list methods (#133)
* access list methods * fix package json * revert package json * bump contracts * try node branch * fix escrow commands * print logs * latest logs * fix compute command * change PK * use main branch * try catch start compute job * log stderr * timeout * revert to verifyfunds * fix info h * cleanup * log cmd
1 parent 52b5a2c commit a445655

File tree

10 files changed

+588
-51
lines changed

10 files changed

+588
-51
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,4 @@ jobs:
136136
if: ${{ failure() }}
137137
run: |
138138
echo "========== Ocean Node Logs =========="
139-
tac ${{ github.workspace }}/ocean-node/ocean-node.log || echo "Log file not found"
139+
docker logs --tail 1000 ocean-node-1 2>&1 | tac || echo "Ocean Node container logs not available"

package-lock.json

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@
4545
},
4646
"dependencies": {
4747
"@oasisprotocol/sapphire-paratime": "^1.3.2",
48-
"@oceanprotocol/contracts": "^2.4.0",
49-
"@oceanprotocol/ddo-js": "^0.1.3",
50-
"@oceanprotocol/lib": "^5.0.0",
48+
"@oceanprotocol/contracts": "^2.4.1",
49+
"@oceanprotocol/ddo-js": "^0.1.4",
50+
"@oceanprotocol/lib": "^5.0.3",
5151
"commander": "^13.1.0",
5252
"cross-fetch": "^3.1.5",
5353
"crypto-js": "^4.1.1",

src/cli.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,5 +490,76 @@ export async function createCLI() {
490490
await commands.getAuthorizationsEscrow(token || options.token, payee || options.payee);
491491
});
492492

493+
program
494+
.command('createAccessList')
495+
.description('Create a new access list contract')
496+
.argument('<name>', 'Name for the access list')
497+
.argument('<symbol>', 'Symbol for the access list')
498+
.argument('[transferable]', 'Whether tokens are transferable (true/false)', 'false')
499+
.argument('[initialUsers]', 'Comma-separated list of initial user addresses', '')
500+
.option('-n, --name <name>', 'Name for the access list')
501+
.option('-s, --symbol <symbol>', 'Symbol for the access list')
502+
.option('-t, --transferable [transferable]', 'Whether tokens are transferable (true/false)', 'false')
503+
.option('-u, --users [initialUsers]', 'Comma-separated list of initial user addresses', '')
504+
.action(async (name, symbol, transferable, initialUsers, options) => {
505+
const { signer, chainId } = await initializeSigner();
506+
const commands = new Commands(signer, chainId);
507+
await commands.createAccessList([
508+
options.name || name,
509+
options.symbol || symbol,
510+
options.transferable || transferable,
511+
options.users || initialUsers
512+
]);
513+
});
514+
515+
program
516+
.command('addToAccessList')
517+
.description('Add user(s) to an access list')
518+
.argument('<accessListAddress>', 'Address of the access list contract')
519+
.argument('<users>', 'Comma-separated list of user addresses to add')
520+
.option('-a, --address <accessListAddress>', 'Address of the access list contract')
521+
.option('-u, --users <users>', 'Comma-separated list of user addresses to add')
522+
.action(async (accessListAddress, users, options) => {
523+
const { signer, chainId } = await initializeSigner();
524+
const commands = new Commands(signer, chainId);
525+
await commands.addToAccessList([
526+
options.address || accessListAddress,
527+
options.users || users
528+
]);
529+
});
530+
531+
program
532+
.command('checkAccessList')
533+
.description('Check if user(s) are on an access list')
534+
.argument('<accessListAddress>', 'Address of the access list contract')
535+
.argument('<users>', 'Comma-separated list of user addresses to check')
536+
.option('-a, --address <accessListAddress>', 'Address of the access list contract')
537+
.option('-u, --users <users>', 'Comma-separated list of user addresses to check')
538+
.action(async (accessListAddress, users, options) => {
539+
const { signer, chainId } = await initializeSigner();
540+
const commands = new Commands(signer, chainId);
541+
await commands.checkAccessList([
542+
options.address || accessListAddress,
543+
options.users || users
544+
]);
545+
});
546+
547+
program
548+
.command('removeFromAccessList')
549+
.description('Remove user(s) from an access list')
550+
.argument('<accessListAddress>', 'Address of the access list contract')
551+
.argument('<users>', 'Comma-separated list of user addresses to remove')
552+
.option('-a, --address <accessListAddress>', 'Address of the access list contract')
553+
.option('-u, --users <users>', 'Comma-separated list of user addresses to remove')
554+
.action(async (accessListAddress, users, options) => {
555+
const { signer, chainId } = await initializeSigner();
556+
const commands = new Commands(signer, chainId);
557+
await commands.removeFromAccessList([
558+
options.address || accessListAddress,
559+
options.users || users
560+
]);
561+
});
562+
563+
493564
return program;
494565
}

src/commands.ts

Lines changed: 178 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ import {
2727
sendTx,
2828
unitsToAmount,
2929
EscrowContract,
30-
getTokenDecimals
30+
getTokenDecimals,
31+
AccesslistFactory,
32+
AccessListContract
3133
} from "@oceanprotocol/lib";
3234
import { Asset } from '@oceanprotocol/ddo-js';
3335
import { Signer, ethers, getAddress } from "ethers";
@@ -729,6 +731,7 @@ export class Commands {
729731
)
730732
console.log("Verifying payment...");
731733
await new Promise(resolve => setTimeout(resolve, 3000))
734+
732735
const validationEscrow = await escrow.verifyFundsForEscrowPayment(
733736
paymentToken,
734737
computeEnv.consumerAddress,
@@ -791,10 +794,10 @@ export class Commands {
791794
null,
792795
null,
793796
// additionalDatasets, only c2d v1
794-
output,
797+
output
795798
);
796799

797-
console.log("compute jobs: ", computeJobs);
800+
console.log("computeJobs: ", computeJobs);
798801

799802
if (computeJobs && computeJobs[0]) {
800803
const { jobId, payment } = computeJobs[0];
@@ -1454,4 +1457,176 @@ export class Commands {
14541457

14551458
return authorizations;
14561459
}
1460+
1461+
public async createAccessList(args: string[]): Promise<void> {
1462+
try {
1463+
const name = args[0];
1464+
const symbol = args[1];
1465+
const transferable = args[2] === 'true';
1466+
const initialUsers = args[3] ? args[3].split(',').map(u => u.trim()) : [];
1467+
1468+
if (!name || !symbol) {
1469+
console.error(chalk.red('Name and symbol are required'));
1470+
return;
1471+
}
1472+
1473+
const config = await getConfigByChainId(Number(this.config.chainId));
1474+
if (!config.AccessListFactory) {
1475+
console.error(chalk.red('Access list factory not found. Check local address.json file'));
1476+
return;
1477+
}
1478+
const accessListFactory = new AccesslistFactory(
1479+
config.AccessListFactory,
1480+
this.signer,
1481+
Number(this.config.chainId)
1482+
);
1483+
1484+
const owner = await this.signer.getAddress();
1485+
const tokenURIs = initialUsers.map(() => 'https://oceanprotocol.com/nft/');
1486+
1487+
console.log(chalk.cyan('Creating new access list...'));
1488+
console.log(`Name: ${name}`);
1489+
console.log(`Symbol: ${symbol}`);
1490+
console.log(`Transferable: ${transferable}`);
1491+
console.log(`Owner: ${owner}`);
1492+
console.log(`Initial users: ${initialUsers.length > 0 ? initialUsers.join(', ') : 'none'}`);
1493+
1494+
const accessListAddress = await accessListFactory.deployAccessListContract(
1495+
name,
1496+
symbol,
1497+
tokenURIs,
1498+
transferable,
1499+
owner,
1500+
initialUsers
1501+
);
1502+
1503+
console.log(chalk.green(`\nAccess list created successfully!`));
1504+
console.log(chalk.green(`Contract address: ${accessListAddress}`));
1505+
} catch (error) {
1506+
console.error(chalk.red('Error creating access list:'), error);
1507+
}
1508+
}
1509+
1510+
public async addToAccessList(args: string[]): Promise<void> {
1511+
try {
1512+
const accessListAddress = args[0];
1513+
const users = args[1].split(',').map(u => u.trim());
1514+
1515+
if (!accessListAddress || users.length === 0) {
1516+
console.error(chalk.red('Access list address and at least one user are required'));
1517+
return;
1518+
}
1519+
1520+
const accessList = new AccessListContract(
1521+
accessListAddress,
1522+
this.signer,
1523+
Number(this.config.chainId)
1524+
);
1525+
1526+
console.log(chalk.cyan(`Adding ${users.length} user(s) to access list...`));
1527+
1528+
if (users.length === 1) {
1529+
const tx = await accessList.mint(users[0], 'https://oceanprotocol.com/nft/');
1530+
await tx.wait();
1531+
console.log(chalk.green(`Successfully added user ${users[0]} to access list`));
1532+
return;
1533+
}
1534+
1535+
const tokenURIs = users.map(() => 'https://oceanprotocol.com/nft/');
1536+
const tx = await accessList.batchMint(users, tokenURIs);
1537+
await tx.wait();
1538+
console.log(chalk.green(`Successfully added ${users.length} users to access list:`));
1539+
users.forEach(user => console.log(` - ${user}`));
1540+
} catch (error) {
1541+
console.error(chalk.red('Error adding users to access list:'), error);
1542+
}
1543+
}
1544+
1545+
1546+
public async checkAccessList(args: string[]): Promise<void> {
1547+
try {
1548+
const accessListAddress = args[0];
1549+
const users = args[1].split(',').map(u => u.trim());
1550+
1551+
if (!accessListAddress || users.length === 0) {
1552+
console.error(chalk.red('Access list address and at least one user are required'));
1553+
return;
1554+
}
1555+
1556+
const accessList = new AccessListContract(
1557+
accessListAddress,
1558+
this.signer,
1559+
Number(this.config.chainId)
1560+
);
1561+
1562+
console.log(chalk.cyan(`Checking access list for ${users.length} user(s)...\n`));
1563+
1564+
for (const user of users) {
1565+
const balance = await accessList.balance(user);
1566+
const hasAccess = Number(balance) > 0;
1567+
1568+
if (hasAccess) {
1569+
console.log(chalk.green(`✓ ${user}: Has access (balance: ${balance})`));
1570+
} else {
1571+
console.log(chalk.red(`✗ ${user}: No access`));
1572+
}
1573+
}
1574+
} catch (error) {
1575+
console.error(chalk.red('Error checking access list:'), error);
1576+
}
1577+
}
1578+
1579+
1580+
public async removeFromAccessList(args: string[]): Promise<void> {
1581+
try {
1582+
const accessListAddress = args[0];
1583+
const users = args[1].split(',').map(u => u.trim());
1584+
1585+
if (!accessListAddress || users.length === 0) {
1586+
console.error(chalk.red('Access list address and at least one user address are required'));
1587+
return;
1588+
}
1589+
1590+
const accessList = new AccessListContract(
1591+
accessListAddress,
1592+
this.signer,
1593+
Number(this.config.chainId)
1594+
);
1595+
1596+
console.log(chalk.cyan(`Removing ${users.length} user(s) from access list...`));
1597+
for (const user of users) {
1598+
const balance = await accessList.balance(user);
1599+
1600+
if (Number(balance) === 0) {
1601+
console.log(chalk.yellow(`⚠ User ${user} is not on the access list, skipping...`));
1602+
continue;
1603+
}
1604+
1605+
const balanceNum = Number(balance);
1606+
const contract = accessList.contract;
1607+
1608+
let removedCount = 0;
1609+
for (let index = 0; index < balanceNum; index++) {
1610+
try {
1611+
const tokenId = await contract.tokenOfOwnerByIndex(user, index);
1612+
const tx = await accessList.burn(Number(tokenId));
1613+
await tx.wait();
1614+
1615+
console.log(chalk.green(`✓ Successfully removed user ${user} (token ID: ${tokenId})`));
1616+
removedCount++;
1617+
} catch (e: any) {
1618+
console.log(chalk.yellow(`⚠ Could not remove token at index ${index} for user ${user}: ${e.message}`));
1619+
}
1620+
}
1621+
1622+
if (removedCount === 0) {
1623+
console.log(chalk.yellow(`⚠ Could not remove any tokens for user ${user}`));
1624+
} else if (removedCount < balanceNum) {
1625+
console.log(chalk.yellow(`⚠ Only removed ${removedCount} of ${balanceNum} tokens for user ${user}`));
1626+
}
1627+
}
1628+
} catch (error) {
1629+
console.error(chalk.red('Error removing users from access list:'), error);
1630+
}
1631+
}
14571632
}

src/helpers.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import * as path from "path";
55
import * as sapphire from '@oasisprotocol/sapphire-paratime';
66
import { Asset, DDO } from '@oceanprotocol/ddo-js';
77
import {
8-
AccesslistFactory,
9-
Aquarius,
8+
AccesslistFactory, Aquarius,
109
Nft,
1110
NftFactory,
1211
ProviderInstance,

0 commit comments

Comments
 (0)