Skip to content

Commit

Permalink
feat(mixin-ownable): add Ownable mixin and test
Browse files Browse the repository at this point in the history
  • Loading branch information
davemneo committed Apr 18, 2019
1 parent 44c7e4a commit a747bc6
Show file tree
Hide file tree
Showing 14 changed files with 168 additions and 207 deletions.
2 changes: 1 addition & 1 deletion gulpfile.js
Expand Up @@ -247,7 +247,7 @@ const globs = {
`!packages/*/src/__e2e__/**/*`,
`!packages/*/src/bin/**/*`,
`!packages/neo-one-developer-tools-frame/src/*.ts`,
`!packages/neo-one-smart-contract-lib/src/*.ts`,
`!packages/neo-one-smart-contract-lib/src/**/*.ts`,
`!packages/neo-one-server-plugin-wallet/src/contracts/*.ts`,
].concat(skipGlobs),
types: ['packages/neo-one-types/**/*', '!packages/neo-one-types/package.json'],
Expand Down

This file was deleted.

@@ -0,0 +1,9 @@
import { Deploy, SmartContract } from '@neo-one/smart-contract';
/*tslint:disable-next-line:no-implicit-dependencies*/
import { Ownable } from '@neo-one/smart-contract-lib';

export class TestOwnable extends Ownable(SmartContract) {
public constructor(protected readonly initialOwner = Deploy.senderAddress) {
super();
}
}

This file was deleted.

Expand Up @@ -2,4 +2,4 @@

exports[`RedToken properties + issue + balanceOf + totalSupply + transfer: transfer consume 1`] = `"0"`;

exports[`RedToken properties + issue + balanceOf + totalSupply + transfer: transfer cost 1`] = `"3.433"`;
exports[`RedToken properties + issue + balanceOf + totalSupply + transfer: transfer cost 1`] = `"3.466"`;
Expand Up @@ -2,8 +2,8 @@

exports[`TestToken properties + issue + balanceOf + totalSupply + transfer: deploy consumed 1`] = `"0"`;

exports[`TestToken properties + issue + balanceOf + totalSupply + transfer: deploy cost 1`] = `"3.021"`;
exports[`TestToken properties + issue + balanceOf + totalSupply + transfer: deploy cost 1`] = `"3.054"`;

exports[`TestToken properties + issue + balanceOf + totalSupply + transfer: transfer consume 1`] = `"0"`;

exports[`TestToken properties + issue + balanceOf + totalSupply + transfer: transfer cost 1`] = `"3.433"`;
exports[`TestToken properties + issue + balanceOf + totalSupply + transfer: transfer cost 1`] = `"3.466"`;

This file was deleted.

@@ -0,0 +1,81 @@
import { common, crypto } from '@neo-one/client-common';
import { SmartContractAny } from '@neo-one/client-core';
import { withContracts } from '@neo-one/smart-contract-test';
import * as path from 'path';

const RECIPIENT = {
PRIVATE_KEY: '7d128a6d096f0c14c3a25a2b0c41cf79661bfcb4a8cc95aaaea28bde4d732344',
PUBLIC_KEY: '02028a99826edc0c97d18e22b6932373d908d323aa7f92656a77ec26e8861699ef',
};

describe('Ownable', () => {
test('deploy + transfer', async () => {
await withContracts<{ testOwnable: SmartContractAny }>(
[
{
filePath: path.resolve(__dirname, '..', '..', '__data__', 'contracts', 'ownership', 'TestOwnable.ts'),
name: 'TestOwnable',
},
],
async ({ client, networkName, testOwnable: smartContract, masterAccountID }) => {
crypto.addPublicKey(
common.stringToPrivateKey(RECIPIENT.PRIVATE_KEY),
common.stringToECPoint(RECIPIENT.PUBLIC_KEY),
);

const deployResult = await smartContract.deploy(masterAccountID.address, { from: masterAccountID });
const deployReceipt = await deployResult.confirmed({ timeoutMS: 2500 });
if (deployReceipt.result.state !== 'HALT') {
throw new Error(deployReceipt.result.message);
}

expect(deployReceipt.result.gasConsumed.toString()).toMatchSnapshot('deploy consumed');
expect(deployReceipt.result.gasCost.toString()).toMatchSnapshot('deploy cost');
expect(deployReceipt.result.value).toBeTruthy();

const [initialOwner, recipient] = await Promise.all([
smartContract.owner.confirmed(),
client.providers.memory.keystore.addUserAccount({
network: networkName,
name: 'recipient',
privateKey: RECIPIENT.PRIVATE_KEY,
}),
]);

expect(initialOwner.result.value).toEqual(masterAccountID.address);

const transferReceipt = await smartContract.transferOwnership.confirmed(recipient.userAccount.id.address, {
from: masterAccountID,
});
const newOwner = await smartContract.owner.confirmed();
if (transferReceipt.result.state !== 'HALT') {
throw new Error(transferReceipt.result.message);
}
expect(newOwner.result.value).toEqual(recipient.userAccount.id.address);

const bogusTransferReceipt = await smartContract.transferOwnership.confirmed(masterAccountID.address, {
from: masterAccountID,
});
expect(bogusTransferReceipt.result.state).toEqual('FAULT');
expect(bogusTransferReceipt.result.value).toBeFalsy();

const finalOwner = await smartContract.owner.confirmed();
expect(finalOwner.result.value).toEqual(recipient.userAccount.id.address);

const renounceReceipt = await smartContract.renounceOwnership.confirmed({
from: recipient.userAccount.id,
});
expect(renounceReceipt.result.state).toEqual('HALT');
const noOwner = await smartContract.owner.confirmed();
expect(noOwner.result.value).toEqual(undefined);

await expect(
smartContract.ownerOrThrow.confirmed({
from: recipient.userAccount.id,
}),
).rejects.toBeDefined();
},
{ deploy: false },
);
});
});
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Ownable deploy + transfer: deploy consumed 1`] = `"0"`;

exports[`Ownable deploy + transfer: deploy cost 1`] = `"1.299"`;
4 changes: 1 addition & 3 deletions packages/neo-one-smart-contract-lib/src/index.ts
@@ -1,4 +1,2 @@
// tslint:disable-next-line export-name
export { NEP5Token } from './NEP5Token';
export { DesignatedCaller } from './ownership/DesignatedCaller';
export { TransferableOwnership } from './ownership/TransferableOwnership';
export { Ownable } from './ownership';
62 changes: 62 additions & 0 deletions packages/neo-one-smart-contract-lib/src/ownership/Ownable.ts
@@ -0,0 +1,62 @@
import { Address, SmartContract, createEventNotifier } from '@neo-one/smart-contract';

const ownershipTransferred = createEventNotifier<Address | undefined, Address>('transferred_ownership', 'from', 'to');

export function Ownable<TBase extends Constructor<SmartContract>>(base: TBase) {
abstract class OwnableClass extends base {
protected abstract initialOwner: Address;
private mutableOwner: Address | undefined = undefined;
private mutableOwnerInitialized = false;

// tslint:disable-next-line:no-any
public constructor(...args: any[]) {
super(args);
}

public get owner(): Address | undefined {
return this.mutableOwner ? this.mutableOwner : this.initializeOwner();
}

public renounceOwnership() {
this.onlyOwner();

this.mutableOwner = undefined;
}

public transferOwnership(to: Address): boolean {
this.onlyOwner();

ownershipTransferred(this.mutableOwner, to);
this.mutableOwner = to;

return true;
}

protected ownerOrThrow(): Address {
const owner = this.owner;

if (owner === undefined) {
throw new Error('no owner');
}

return owner;
}

protected onlyOwner() {
if (this.mutableOwner && !Address.isCaller(this.mutableOwner)) {
throw new Error('not owner');
}
}

private initializeOwner() {
if (!this.mutableOwner && !this.mutableOwnerInitialized) {
this.mutableOwner = this.initialOwner;
this.mutableOwnerInitialized = true;
}

return this.mutableOwner;
}
}

return OwnableClass;
}

This file was deleted.

2 changes: 2 additions & 0 deletions packages/neo-one-smart-contract-lib/src/ownership/index.ts
@@ -0,0 +1,2 @@
// tslint:disable: export-name
export { Ownable } from './Ownable';

0 comments on commit a747bc6

Please sign in to comment.