Function to compute nth root

This commit is contained in:
Greg Hysen
2019-05-23 10:46:49 -07:00
parent 9787cf8296
commit 95b284d648
8 changed files with 139 additions and 3 deletions

View File

@@ -0,0 +1,75 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
library LibMath {
function _nthRoot(uint256 base, uint256 n) internal pure returns (uint256 root) {
assembly {
///// Implements Newton's Approximation, derived from Newton's nth Root Algorithm /////
// See https://en.wikipedia.org/wiki/Nth_root#nth_root_algorithm
// 1. Find greatest power-of-2 <= `value`
let m := 0
for {let v := base}
gt(v, 0)
{v := shr(1, v)}
{
m := add(m, 1)
}
m := sub(m, 1)
// 2. Find greatest power-of-2 that, when raised to the power of `n`,
// is <= `value`
let x := exp(2, div(m, n))
// 3. Find y such that `x` + `y` = `value`
let y := xor(base, exp(2, mul(div(m, n), n)))
// 4. Run Newton's Approximation to find the root
root := add(x, div(y, mul(n, exp(2, mul(div(m, n), sub(n, 1))))))
/**
* Note 1:
* On some clients (like ganache), execution freezes when running exponentiation
* with a dynamically generated base. Because of this, all exponentiation above
* is done base-2.
* Example:
* Call the solidity function below with `n >= 1` and execution will timeout.
*
* function repro(uint256 n) public pure returns (uint256) {
* uint256 m = 2**n;
* return m**n;
* }
*
* Call a similar function with the same input _does not_ timeout:
*
* function fix(uint256 n) public pure returns (uint256) {
* uint256 m = 2**(n*n);
* return m;
* }
*/
}
}
}

View File

@@ -0,0 +1,33 @@
/*
Copyright 2018 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.5;
import "../src/libs/LibMath.sol";
contract LibMathTest {
function nthRoot(uint256 base, uint256 n) public pure returns (uint256 root) {
return LibMath._nthRoot(base, n);
}
}

View File

@@ -35,7 +35,7 @@
"compile:truffle": "truffle compile"
},
"config": {
"abis": "./generated-artifacts/@(LibZrxToken|MixinStake|MixinVaultCore|Staking|ZrxVault).json",
"abis": "./generated-artifacts/@(LibMathTest|LibZrxToken|MixinStake|MixinVaultCore|Staking|ZrxVault).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {

View File

@@ -5,6 +5,7 @@
*/
import { ContractArtifact } from 'ethereum-types';
import * as LibMathTest from '../generated-artifacts/LibMathTest.json';
import * as LibZrxToken from '../generated-artifacts/LibZrxToken.json';
import * as MixinStake from '../generated-artifacts/MixinStake.json';
import * as MixinVaultCore from '../generated-artifacts/MixinVaultCore.json';
@@ -16,4 +17,5 @@ export const artifacts = {
LibZrxToken: LibZrxToken as ContractArtifact,
MixinVaultCore: MixinVaultCore as ContractArtifact,
ZrxVault: ZrxVault as ContractArtifact,
LibMathTest: LibMathTest as ContractArtifact,
};

View File

@@ -3,6 +3,7 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/lib_math_test';
export * from '../generated-wrappers/lib_zrx_token';
export * from '../generated-wrappers/mixin_stake';
export * from '../generated-wrappers/mixin_vault_core';

View File

@@ -108,6 +108,13 @@ describe('Staking Core', () => {
expect(zrxTokenBalanceOfStakerAfterStaking).to.be.bignumber.equal(zrxTokenBalanceOfStakerBeforeStaking.minus(amountToStake).plus(amountToUnstake));
}
});
it('nth root', async () => {
const base = new BigNumber(1419857);
const n = new BigNumber(5);
const root = await stakingWrapper.nthRoot(base, n);
expect(root).to.be.bignumber.equal(17);
});
});
});
// tslint:enable:no-unnecessary-type-assertion

View File

@@ -8,7 +8,7 @@ import { DummyERC20TokenContract } from '@0x/contracts-erc20';
import { ERC20ProxyContract } from '@0x/contracts-asset-proxy';
import * as _ from 'lodash';
import { artifacts, StakingContract, ZrxVaultContract } from '../../src';
import { artifacts, StakingContract, ZrxVaultContract, LibMathTestContract } from '../../src';
const expect = chai.expect;
@@ -21,6 +21,7 @@ export class StakingWrapper {
private readonly _zrxTokenContract: DummyERC20TokenContract;
private _stakingContractIfExists?: StakingContract;
private _zrxVaultContractIfExists?: ZrxVaultContract;
private _libMathTestContractIfExists?: LibMathTestContract;
constructor(provider: Provider, ownerAddres: string, erc20ProxyContract: ERC20ProxyContract, zrxTokenContract: DummyERC20TokenContract) {
this._web3Wrapper = new Web3Wrapper(provider);
@@ -38,6 +39,10 @@ export class StakingWrapper {
this._validateDeployedOrThrow();
return this._zrxVaultContractIfExists as ZrxVaultContract;
}
public getLibMathTestContract(): LibMathTestContract {
this._validateDeployedOrThrow();
return this._libMathTestContractIfExists as LibMathTestContract;
}
public async deployAndConfigureContracts(): Promise<void> {
// deploy zrx vault
const zrxAssetData = assetDataUtils.encodeERC20AssetData(this._zrxTokenContract.address);
@@ -59,7 +64,13 @@ export class StakingWrapper {
(this._zrxVaultContractIfExists as ZrxVaultContract).address
);
// set staking contract in zrx vault
await this.getZrxVaultContract().setStakingContractAddrsess.awaitTransactionSuccessAsync((this._stakingContractIfExists as StakingContract).address);
await (this._zrxVaultContractIfExists as ZrxVaultContract).setStakingContractAddrsess.awaitTransactionSuccessAsync((this._stakingContractIfExists as StakingContract).address);
// deploy libmath test
this._libMathTestContractIfExists = await LibMathTestContract.deployFrom0xArtifactAsync(
artifacts.LibMathTest,
this._provider,
txDefaults,
);
}
public async stake(holder: string, amount: BigNumber): Promise<BigNumber> {
const stakeMinted = await this.getStakingContract().stake.callAsync(amount, {from: holder});
@@ -87,6 +98,10 @@ export class StakingWrapper {
const balance = await this._zrxTokenContract.balanceOf.callAsync(this.getZrxVaultContract().address);
return balance;
}
public async nthRoot(value: BigNumber, n: BigNumber): Promise<BigNumber> {
const output = await this.getLibMathTestContract().nthRoot.callAsync(value, n);
return output;
}
public toBaseUnitAmount(amount: BigNumber | number): BigNumber {
const decimals = 18;
const amountAsBigNumber = typeof(amount) === 'number' ? new BigNumber(amount) : amount;
@@ -97,6 +112,8 @@ export class StakingWrapper {
throw new Error('Staking contract not deployed. Call `deployStakingContracts`');
} else if (this._zrxVaultContractIfExists === undefined) {
throw new Error('ZRX Vault contract not deployed. Call `deployStakingContracts`');
} else if (this._libMathTestContractIfExists === undefined) {
throw new Error('LibMathTest contract not deployed. Call `deployStakingContracts`');
}
}
}

View File

@@ -3,6 +3,7 @@
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/LibMathTest.json",
"generated-artifacts/LibZrxToken.json",
"generated-artifacts/MixinStake.json",
"generated-artifacts/MixinVaultCore.json",