334 lines
12 KiB
TypeScript
334 lines
12 KiB
TypeScript
|
// @notice : run the typechain generate commance into the smrt contract repository (truffle in our case), after the contracts are compiled.
|
||
|
import { ERC3475Instance } from "../types/truffle-contracts";
|
||
|
|
||
|
function sleep(ms: any) {
|
||
|
return new Promise((resolve) => {
|
||
|
setTimeout(resolve, ms);
|
||
|
});
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
const Bond = artifacts.require("ERC3475");
|
||
|
|
||
|
contract('Bond', async (accounts: string[]) => {
|
||
|
|
||
|
let bondContract: ERC3475Instance;
|
||
|
const lender = accounts[1];
|
||
|
const operator = accounts[2];
|
||
|
const secondaryMarketBuyer = accounts[3];
|
||
|
const secondaryMarketBuyer2 = accounts[4];
|
||
|
const spender = accounts[5];
|
||
|
|
||
|
const DBITClassId: number = 0;
|
||
|
const firstNonceId: number = 0;
|
||
|
|
||
|
interface _transaction {
|
||
|
classId: string | number | BN;
|
||
|
nonceId: string | number | BN;
|
||
|
amount: string | number | BN;
|
||
|
}
|
||
|
|
||
|
before('testing', async () => {
|
||
|
bondContract = await Bond.deployed();
|
||
|
|
||
|
})
|
||
|
|
||
|
it('should issue bonds to a lender', async () => {
|
||
|
let _transactionIssuer: _transaction[]
|
||
|
=
|
||
|
[{
|
||
|
classId: DBITClassId,
|
||
|
nonceId: firstNonceId,
|
||
|
amount: 7000
|
||
|
}];
|
||
|
|
||
|
await bondContract.issue(lender, _transactionIssuer, { from: accounts[0] })
|
||
|
await bondContract.issue(lender, _transactionIssuer, { from: accounts[0] })
|
||
|
const balance = (await bondContract.balanceOf(lender, DBITClassId, firstNonceId)).toNumber()
|
||
|
const activeSupply = (await bondContract.activeSupply(DBITClassId, firstNonceId)).toNumber()
|
||
|
assert.equal(balance, 14000);
|
||
|
assert.equal(activeSupply, 14000);
|
||
|
})
|
||
|
it('lender should be able to transfer bonds to another address', async () => {
|
||
|
|
||
|
const transferBonds: _transaction[] = [
|
||
|
{
|
||
|
classId: DBITClassId,
|
||
|
nonceId: firstNonceId,
|
||
|
amount: 2000
|
||
|
}];
|
||
|
await bondContract.transferFrom(lender, secondaryMarketBuyer, transferBonds, { from: lender })
|
||
|
|
||
|
const lenderBalance = (await bondContract.balanceOf(lender, DBITClassId, firstNonceId)).toNumber()
|
||
|
const secondaryBuyerBalance = (await bondContract.balanceOf(secondaryMarketBuyer, DBITClassId, firstNonceId)).toNumber()
|
||
|
const activeSupply = (await bondContract.activeSupply(DBITClassId, firstNonceId)).toNumber()
|
||
|
|
||
|
assert.equal(lenderBalance, 12000);
|
||
|
assert.equal(secondaryBuyerBalance, 2000);
|
||
|
assert.equal(activeSupply, 14000);
|
||
|
})
|
||
|
it('operator should be able to manipulate bonds after approval', async () => {
|
||
|
const transactionApproval: _transaction[] = [
|
||
|
{
|
||
|
classId: DBITClassId,
|
||
|
nonceId: firstNonceId,
|
||
|
amount: 2000
|
||
|
}];
|
||
|
|
||
|
await bondContract.setApprovalFor(operator, true, { from: lender })
|
||
|
const isApproved = await bondContract.isApprovedFor(lender, operator);
|
||
|
assert.isTrue(isApproved);
|
||
|
await bondContract.transferFrom(lender, secondaryMarketBuyer2, transactionApproval, { from: operator })
|
||
|
expect((await bondContract.balanceOf(lender, DBITClassId, firstNonceId)).toNumber()).to.equal(10000);
|
||
|
expect((await bondContract.balanceOf(secondaryMarketBuyer2, DBITClassId, firstNonceId)).toNumber()).to.equal(2000);
|
||
|
|
||
|
})
|
||
|
|
||
|
it('lender should redeem bonds when conditions are met', async () => {
|
||
|
const redemptionTransaction: _transaction[] = [
|
||
|
{
|
||
|
classId: 1,
|
||
|
nonceId: 1,
|
||
|
amount: 2000
|
||
|
|
||
|
},
|
||
|
];
|
||
|
await bondContract.issue(accounts[2],redemptionTransaction, {from: accounts[2]});
|
||
|
assert.equal((await bondContract.balanceOf(accounts[2], 1, 1)).toNumber(), 2000);
|
||
|
// adding delay for passing the redemption time period.
|
||
|
await sleep(7000);
|
||
|
|
||
|
await bondContract.redeem(accounts[2], redemptionTransaction, {from:accounts[2]});
|
||
|
|
||
|
assert.equal((await bondContract.balanceOf(accounts[2], DBITClassId, firstNonceId)).toNumber(), 0);
|
||
|
})
|
||
|
|
||
|
|
||
|
it('lender should not be able to redeem bonds when conditions are not met', async () => {
|
||
|
const redemptionTransaction: _transaction[] = [
|
||
|
|
||
|
{
|
||
|
classId: 0,
|
||
|
nonceId: 0,
|
||
|
amount: 2000
|
||
|
|
||
|
},
|
||
|
|
||
|
];
|
||
|
|
||
|
await bondContract.issue(accounts[2],redemptionTransaction, {from: accounts[2]});
|
||
|
assert.equal((await bondContract.balanceOf(accounts[2], 0, 0)).toNumber(), 2000);
|
||
|
try {
|
||
|
await bondContract.redeem(accounts[2], redemptionTransaction, {from:accounts[2]});
|
||
|
}
|
||
|
catch(e:any){
|
||
|
assert.isTrue(true);
|
||
|
}
|
||
|
|
||
|
})
|
||
|
//////////////////// UNIT TESTS //////////////////////////////
|
||
|
|
||
|
it('should transfer bonds from an caller address to another', async () => {
|
||
|
const transactionTransfer: _transaction[] = [
|
||
|
{
|
||
|
classId: DBITClassId,
|
||
|
nonceId: firstNonceId,
|
||
|
amount: 500
|
||
|
}];
|
||
|
await bondContract.issue(lender, transactionTransfer, { from: lender });
|
||
|
const tx = (await bondContract.transferFrom(lender, secondaryMarketBuyer, transactionTransfer, {from:lender})).tx;
|
||
|
console.log(tx)
|
||
|
assert.isString(tx);
|
||
|
})
|
||
|
|
||
|
it('should issue bonds to a given address', async () => {
|
||
|
|
||
|
const transactionIssue: _transaction[] = [
|
||
|
{
|
||
|
classId: 1,
|
||
|
nonceId: firstNonceId,
|
||
|
amount: 500
|
||
|
|
||
|
}
|
||
|
];
|
||
|
const tx = (await bondContract.issue(lender, transactionIssue)).tx;
|
||
|
console.log(tx)
|
||
|
assert.isString(tx);
|
||
|
})
|
||
|
|
||
|
it('should redeem bonds from a given address', async () => {
|
||
|
const transactionRedeem: _transaction[] = [
|
||
|
{
|
||
|
classId: 1,
|
||
|
nonceId: firstNonceId,
|
||
|
amount: 500
|
||
|
|
||
|
}];
|
||
|
await bondContract.issue(lender, transactionRedeem, {from: lender});
|
||
|
sleep(7000);
|
||
|
|
||
|
const tx = (await bondContract.redeem(lender, transactionRedeem, {from:lender})).tx;
|
||
|
|
||
|
console.log(tx)
|
||
|
assert.isString(tx);
|
||
|
})
|
||
|
|
||
|
it('should burn bonds from a given address', async () => {
|
||
|
const transactionRedeem: _transaction[] = [
|
||
|
{
|
||
|
classId: DBITClassId,
|
||
|
nonceId: firstNonceId,
|
||
|
amount: 500
|
||
|
}];
|
||
|
|
||
|
await bondContract.issue(lender, transactionRedeem, {from: lender});
|
||
|
const tx = (await bondContract.burn(lender, transactionRedeem, {from:lender})).tx;
|
||
|
console.log(tx)
|
||
|
assert.isString(tx);
|
||
|
})
|
||
|
|
||
|
it('should approve spender to manage a given amount of bonds from the caller address', async () => {
|
||
|
const transactionApprove: _transaction[] = [
|
||
|
{
|
||
|
classId: DBITClassId,
|
||
|
nonceId: firstNonceId,
|
||
|
amount: 500
|
||
|
}];
|
||
|
|
||
|
await bondContract.issue(lender, transactionApprove, {from: lender});
|
||
|
const tx = (await bondContract.approve(spender, transactionApprove)).tx;
|
||
|
console.log(tx)
|
||
|
assert.isString(tx);
|
||
|
})
|
||
|
|
||
|
it('setApprovalFor (called by bond owner) should be able to give operator permissions to manage bonds for given classId', async () => {
|
||
|
const tx = (await bondContract.setApprovalFor(operator, true, { from: lender })).tx;
|
||
|
console.log(tx)
|
||
|
assert.isString(tx);
|
||
|
})
|
||
|
|
||
|
it('should batch approve', async () => {
|
||
|
|
||
|
const transactionApprove: _transaction[] = [
|
||
|
{
|
||
|
classId: DBITClassId,
|
||
|
nonceId: firstNonceId,
|
||
|
amount: 500
|
||
|
},
|
||
|
{ classId: 1, nonceId: 0, amount: 900 }
|
||
|
|
||
|
];
|
||
|
|
||
|
|
||
|
await await bondContract.issue(spender,transactionApprove, {from:spender});
|
||
|
const tx = (await bondContract.approve(spender, transactionApprove, {from:spender})).tx;
|
||
|
console.log(tx)
|
||
|
assert.isString(tx);
|
||
|
})
|
||
|
|
||
|
it('should return the active supply', async () => {
|
||
|
const activeSupply = (await bondContract.activeSupply(DBITClassId, firstNonceId)).toNumber();
|
||
|
console.log(activeSupply)
|
||
|
assert.isNumber(activeSupply);
|
||
|
})
|
||
|
|
||
|
it('should return the redeemed supply', async () => {
|
||
|
const redeemedSupply = (await bondContract.redeemedSupply(DBITClassId, firstNonceId)).toNumber();
|
||
|
console.log(redeemedSupply)
|
||
|
assert.isNumber(redeemedSupply);
|
||
|
})
|
||
|
|
||
|
it('should return the burned supply', async () => {
|
||
|
const burnedSupply = (await bondContract.burnedSupply(DBITClassId, firstNonceId)).toNumber();
|
||
|
console.log(burnedSupply)
|
||
|
assert.isNumber(burnedSupply);
|
||
|
})
|
||
|
|
||
|
it('should return the total supply', async () => {
|
||
|
const totalSupply = (await bondContract.totalSupply(DBITClassId, firstNonceId)).toNumber();
|
||
|
console.log(totalSupply)
|
||
|
assert.isNumber(totalSupply);
|
||
|
})
|
||
|
|
||
|
it('should return the balanceOf a bond of a given address', async () => {
|
||
|
const balanceOf = (await bondContract.balanceOf(lender, DBITClassId, firstNonceId)).toNumber();
|
||
|
console.log(balanceOf)
|
||
|
assert.isNumber(balanceOf);
|
||
|
})
|
||
|
|
||
|
it('should return the symbol of a class of bond', async () => {
|
||
|
let metadataId = 0;
|
||
|
const symbol: {
|
||
|
stringValue: string;
|
||
|
uintValue: BN;
|
||
|
addressValue: string;
|
||
|
boolValue: boolean;
|
||
|
} = (await bondContract.classValues(DBITClassId, metadataId));
|
||
|
console.log(JSON.stringify(symbol));
|
||
|
assert.isString(symbol.stringValue);
|
||
|
})
|
||
|
|
||
|
it('should return the Values for given bond class', async () => {
|
||
|
const metadataId = 0;
|
||
|
|
||
|
let _transactionIssuer: _transaction[]
|
||
|
=
|
||
|
[{
|
||
|
classId: DBITClassId,
|
||
|
nonceId: firstNonceId,
|
||
|
amount: 7000
|
||
|
}];
|
||
|
|
||
|
await bondContract.issue(lender, _transactionIssuer, { from: accounts[0] })
|
||
|
const valuesClass = (await bondContract.classValues(DBITClassId, metadataId));
|
||
|
console.log("class infos: ", JSON.stringify(valuesClass));
|
||
|
assert.isString(valuesClass.toString());
|
||
|
})
|
||
|
|
||
|
it('should return the infos of a nonce for given bond class', async () => {
|
||
|
const metadataId = 0;
|
||
|
const infos = (await bondContract.nonceValues(DBITClassId, firstNonceId, metadataId));
|
||
|
console.log("nonce infos: ", JSON.stringify(infos))
|
||
|
assert.isString(infos.toString());
|
||
|
})
|
||
|
|
||
|
it('should return if an operator is approved on a class and nonce given for an address', async () => {
|
||
|
const isApproved = (await bondContract.isApprovedFor(lender, operator));
|
||
|
console.log("operator is Approved? : ", isApproved)
|
||
|
assert.isBoolean(isApproved);
|
||
|
})
|
||
|
|
||
|
it('should return if its redeemable', async () => {
|
||
|
let _transactionIssuer: _transaction[]
|
||
|
=
|
||
|
[{
|
||
|
classId: 1,
|
||
|
nonceId: 1,
|
||
|
amount: 7000
|
||
|
}];
|
||
|
|
||
|
await bondContract.issue(accounts[1], _transactionIssuer, { from: accounts[1] })
|
||
|
const getProgress = await bondContract.getProgress(1,1);
|
||
|
console.log("is Redeemable? : ", getProgress[1].toNumber() >= 0)
|
||
|
assert.isNumber(getProgress[1].toNumber());
|
||
|
|
||
|
})
|
||
|
|
||
|
it('should set allowance of a spender', async () => {
|
||
|
|
||
|
const allowance = (await bondContract.allowance(lender, spender, DBITClassId, firstNonceId, {from:lender})).toNumber();
|
||
|
console.log("allowance : ", allowance)
|
||
|
assert.isNumber(allowance);
|
||
|
})
|
||
|
|
||
|
it('should return if operator is approved for', async () => {
|
||
|
const isApproved = (await bondContract.isApprovedFor(lender, operator));
|
||
|
console.log("operator is Approved? : ", isApproved)
|
||
|
assert.isTrue(isApproved);
|
||
|
})
|
||
|
|
||
|
});
|