Skip to content

Commit

Permalink
add
Browse files Browse the repository at this point in the history
  • Loading branch information
lidangzzz committed May 10, 2023
1 parent 473ae21 commit 2027d8f
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import "../../../Plugin/PluginSystem.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "../../../Plugin/Plugin.sol";
import "../../../Utilities/ErrorMsg.sol";
import "../../../TokenOwnerListManager.sol";

/**
* @title Implementation of all token-related operation
* @author DARC Team
* @notice null
*/

contract TokenInstructions is MachineStateManager{
contract TokenInstructions is MachineStateManager, TokenOwnerListManager{
/**
* @notice The function that executes the BATCH_MINT_TOKENS operation
* @param operation the operation index to be executed
Expand Down Expand Up @@ -59,6 +60,21 @@ contract TokenInstructions is MachineStateManager{
require(bIsValid, "The balance of the token is overflow");
}
}

// then update the token owner list for each token level
// first get all the token levels
uint256[] memory uniqueTokenLevelList = removeDuplicateIntFromArray(tokenClass);
address[] memory uniqueTarget = removeDuplicateAddressFromArray(target);

// then go through each token level and update the token owner list
address[] memory empty = new address[](0);
for (uint256 i = 0; i < uniqueTokenLevelList.length; i++) {
if (bIsSandbox) {
updateTokenOwnerList(true, uniqueTarget, empty, uniqueTokenLevelList[i]);
} else {
updateTokenOwnerList(false, uniqueTarget, empty, uniqueTokenLevelList[i]);
}
}
}

/**
Expand Down
213 changes: 213 additions & 0 deletions darc-protocol/contracts/TokenOwnerListManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.9;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./MachineState.sol";
import "./Plugin/Plugin.sol";
import "./MachineStateManager.sol";

/**
* @title Token Owner List Manager
* @notice null
*/

contract TokenOwnerListManager is MachineStateManager {
/**
* @notice This is the core protocol that add new token owners to the token owner list
* and remove the token owners from the token owner list if the balance of the token owner is zero
* for token level = tokenLevel.
*
* This is because some of owners transfer their tokens to others, so there are some new owners whose
* balance is not zero, and some old owners whose balance is zero.
*
* This function is called after the operation or mint/burn/transfer/transferFrom/
* pay_to_mint/pay_to_transfer is executed successfully.
*
* The reason of this function is to update the token owner list of each certain token level efficiently,
* which provides an up-to-date list of keys for token balance mapping.
*
* @param bIsSandbox The flag to indicate whether the operation is in the sandbox
* @param addOwnerList The list of owner addresses which receive more tokens
* @param removeOwnerList The list of owner addresses which transfer all tokens to others and balance is (probably) zero
* @param tokenLevel The level of the token
*/
function updateTokenOwnerList(bool bIsSandbox, address[] memory addOwnerList, address[] memory removeOwnerList, uint256 tokenLevel) internal {
if (bIsSandbox) {

// 1. Initialize two lists: toAddInit and toRemoveInit
address[] memory toAdd = new address[](addOwnerList.length);
uint256 toAddIndex = 0;
address[] memory toRemove = new address[](removeOwnerList.length);
uint256 toRemoveIndex = 0;



// 2. Check if the token owner list contains any address in the addOwnerList,
// if any address in the addOwnerList is not in the token owner list,
// and the balance of this address is not zero, then just add it to the toAdd list
for (uint256 index = 0; index < addOwnerList.length; index++) {
if ((!tokenOwnerListContainsKeyAddress(bIsSandbox, tokenLevel, addOwnerList[index]))
&& (sandboxMachineState.tokenList[tokenLevel].tokenBalance[addOwnerList[index]] > 0)
) {
toAdd[toAddIndex] = addOwnerList[index];
toAddIndex++;
}
}

// 3. Check if the the addresses in removeOwnerList are with zero balance,
// and if so, add them to the toRemove list
for (uint256 index = 0; index < removeOwnerList.length; index++) {
if (sandboxMachineState.tokenList[tokenLevel].tokenBalance[removeOwnerList[index]] == 0) {
toRemove[toRemoveIndex] = removeOwnerList[index];
toRemoveIndex++;
}
}

//4. construct the final list with all items from toRemove removed and all items from toAdd added
address[] memory finalList = new address[](sandboxMachineState.tokenList[tokenLevel].ownerList.length - toRemoveIndex + toAddIndex);
uint256 pt = 0;
for (uint256 index = 0; index < sandboxMachineState.tokenList[tokenLevel].ownerList.length; index++) {
if (!inArray(toRemove, sandboxMachineState.tokenList[tokenLevel].ownerList[index])) {
finalList[pt] = sandboxMachineState.tokenList[tokenLevel].ownerList[index];
pt++;
}
}

for (uint256 index = 0; index < toAddIndex; index++) {
finalList[pt] = toAdd[index];
pt++;
}

// 5. Update the token owner list
sandboxMachineState.tokenList[tokenLevel].ownerList = finalList;

} else {

// 1. Initialize two lists: toAddInit and toRemoveInit
address[] memory toAdd = new address[](addOwnerList.length);
uint256 toAddIndex = 0;
address[] memory toRemove = new address[](removeOwnerList.length);
uint256 toRemoveIndex = 0;

// 2. Check if the token owner list contains any address in the addOwnerList,
// if any address in the addOwnerList is not in the token owner list,
// and the balance of this address is not zero, then just add it to the toAdd list
for (uint256 index = 0; index < addOwnerList.length; index++) {
if ((!tokenOwnerListContainsKeyAddress(bIsSandbox, tokenLevel, addOwnerList[index]))
&& (currentMachineState.tokenList[tokenLevel].tokenBalance[addOwnerList[index]] > 0)
) {
toAdd[toAddIndex] = addOwnerList[index];
toAddIndex++;
}
}

// 3. Check if the the addresses in removeOwnerList are with zero balance,
// and if so, add them to the toRemove list
for (uint256 index = 0; index < removeOwnerList.length; index++) {
if (currentMachineState.tokenList[tokenLevel].tokenBalance[removeOwnerList[index]] == 0) {
toRemove[toRemoveIndex] = removeOwnerList[index];
toRemoveIndex++;
}
}

//4. construct the final list with all items from toRemove removed and all items from toAdd added
address[] memory finalList = new address[](currentMachineState.tokenList[tokenLevel].ownerList.length - toRemoveIndex + toAddIndex);
uint256 pt = 0;
for (uint256 index = 0; index < currentMachineState.tokenList[tokenLevel].ownerList.length; index++) {
if (!inArray(toRemove, currentMachineState.tokenList[tokenLevel].ownerList[index])) {
finalList[pt] = currentMachineState.tokenList[tokenLevel].ownerList[index];
pt++;
}
}

for (uint256 index = 0; index < toAddIndex; index++) {
finalList[pt] = toAdd[index];
pt++;
}

// 5. Update the token owner list
currentMachineState.tokenList[tokenLevel].ownerList = finalList;
}
}

/**
* Check if the token owner list contains the key address
* @param bIsSandbox If the operation is in the sandbox
* @param tokenLevel The level of the token
* @param key The key address
*/
function tokenOwnerListContainsKeyAddress(bool bIsSandbox, uint256 tokenLevel, address key) view internal returns (bool) {
if (bIsSandbox) {
for (uint256 index = 0; index < sandboxMachineState.tokenList[tokenLevel].ownerList.length; index++) {
if (sandboxMachineState.tokenList[tokenLevel].ownerList[index] == key) {
return true;
}
}
return false;
} else {
for (uint256 index = 0; index < currentMachineState.tokenList[tokenLevel].ownerList.length; index++) {
if (currentMachineState.tokenList[tokenLevel].ownerList[index] == key) {
return true;
}
}
return false;
}
}

/**
* The function to check if the address is in the list
* @param array The list to be checked
* @param key The key address to be checked
*/
function inArray(address[] memory array, address key) pure internal returns (bool) {
for (uint256 index = 0; index < array.length; index++) {
if (array[index] == key) {
return true;
}
}
return false;
}

/**
* The function to remove the duplicate address from the array
* @param array The array to be checked
*/
function removeDuplicateAddressFromArray(address[] memory array) pure internal returns (address[] memory) {
uint256 length = array.length;
for (uint256 i = 0; i < length - 1; i++) {
for (uint256 j = i + 1; j < length; j++) {
if (array[i] == array[j]) {
array[j] = array[length - 1];
length--;
j--;
}
}
}
address[] memory arrayNew = new address[](length);
for (uint256 i = 0; i < length; i++) {
arrayNew[i] = array[i];
}
return arrayNew;
}

/**
* The function to remove the duplicate uint256 from the array
* @param array The array to be checked
*/
function removeDuplicateIntFromArray(uint256[] memory array) pure internal returns (uint256[] memory) {
uint256 length = array.length;
for (uint256 i = 0; i < length - 1; i++) {
for (uint256 j = i + 1; j < length; j++) {
if (array[i] == array[j]) {
array[j] = array[length - 1];
length--;
j--;
}
}
}
uint256[] memory arrayNew = new uint256[](length);
for (uint256 i = 0; i < length; i++) {
arrayNew[i] = array[i];
}
return arrayNew;
}
}
47 changes: 42 additions & 5 deletions darc-protocol/test/operationUnitTest/batch_mint_token_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { BigNumber } from "ethers";

// test for batch mint token instruction on DARC

describe("batch_mint_token_test", function () {
describe.only("batch_mint_token_test", function () {


it ("should mint tokens", async function () {
Expand Down Expand Up @@ -60,11 +60,11 @@ describe("batch_mint_token_test", function () {
PARAMETER_ARRAY: [],
PLUGIN_ARRAY: [],
UINT256_2DARRAY: [
[BigNumber.from(0), BigNumber.from(1)], // token class = 0
[BigNumber.from(100), BigNumber.from(200)], // amount = 100
[BigNumber.from(0), BigNumber.from(1), BigNumber.from(0), BigInt(0)], // token class = 0
[BigNumber.from(100), BigNumber.from(200), BigNumber.from(300), BigInt(16)], // amount = 100
],
ADDRESS_2DARRAY: [
[programOperatorAddress,programOperatorAddress], // to = programOperatorAddress
[programOperatorAddress,programOperatorAddress, programOperatorAddress, programOperatorAddress], // to = programOperatorAddress
]
}
}],
Expand All @@ -74,7 +74,44 @@ describe("batch_mint_token_test", function () {
const balance0 = await darc.getTokenOwnerBalance(0, programOperatorAddress);
const balance1 = await darc.getTokenOwnerBalance(1, programOperatorAddress);

expect(balance0.toBigInt().toString()).to.equal("100");
expect(balance0.toBigInt().toString()).to.equal("416");
expect(balance1.toBigInt().toString()).to.equal("200");
expect(await darc.getTokenOwners(0)).to.have.lengthOf(1);
expect(await darc.getTokenOwners(1)).to.have.lengthOf(1);


// batch mint token to another addresses
const addr1 = "0x90F79bf6EB2c4f870365E785982E1f101E93b906";
const addr2 = '0x976EA74026E726554dB657fA54763abd0C3a0aa9';
const addr3 = '0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199';

const result_entrance2 = darc.entrance({
programOperatorAddress: programOperatorAddress,
operations: [{
operatorAddress: programOperatorAddress,
opcode: 1, // mint token
param: {
UINT256_ARRAY: [],
ADDRESS_ARRAY: [],
STRING_ARRAY: [],
BOOL_ARRAY: [],
VOTING_RULE_ARRAY: [],
PARAMETER_ARRAY: [],
PLUGIN_ARRAY: [],
UINT256_2DARRAY: [
[BigNumber.from(0), BigNumber.from(0), BigNumber.from(0), BigNumber.from(0)], // token class = 0
[BigNumber.from(100), BigNumber.from(200), BigNumber.from(300), BigNumber.from(16)], // amount = 100
],
ADDRESS_2DARRAY: [
[addr1,addr2, addr3, programOperatorAddress], // to = programOperatorAddress
]
}
}],
});

console.log("Token owner list of token 0 after op: ");
console.log(await darc.getTokenOwners(0));
console.log("Token owner list of token 1 after op: ");
console.log(await darc.getTokenOwners(1));
});
});

0 comments on commit 2027d8f

Please sign in to comment.