Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solving issue #1905: Aggregate Fights into a single txn to reduce gas… #1906

Merged
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ This code is open, but not open source. It is not licensed, which means you cann
## Currency Setup

1. Install [Ganache](https://www.trufflesuite.com/ganache).
1. For Ganache, choose Quickstart Ethereum.
1. Increase the gas limit in the workspace to `99999999` (or some other high number so you can deploy).
1. Install [MetaMask](https://metamask.io/).
1. Create a new connection to connect to Ganache with these settings: http://localhost:7545, any name, any chain id
1. In Ganache, click the key icon on the right side of any address and grab the private key.
1. In MetaMask, create a new account, import from private key, and paste the key in there.
2. For Ganache, choose Quickstart Ethereum.
3. Increase the gas limit in the workspace to `99999999` (or some other high number so you can deploy).
4. Install [MetaMask](https://metamask.io/).
5. Create a new connection to connect to Ganache with these settings: http://localhost:7545, any name, any chain id
6. In Ganache, click the key icon on the right side of any address and grab the private key. Make sure that chainID is 5777.
7. In MetaMask, create a new account, import from private key, and paste the key in there.

You should now have 100 fake eth! You're now fake rich.

Expand Down Expand Up @@ -118,7 +118,6 @@ If you get any issues during deployment, run:
- Language is loaded on startup and added to the language drop-down of the Options page.
- The value for the drop-down is "name" at the root of the json map.


### i18n Manager App

- Adding translations is easier with the use of [i18n-manager](https://www.electronjs.org/apps/i18n-manager)
Expand Down
72 changes: 59 additions & 13 deletions contracts/TokensManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ contract TokensManager is Initializable, AccessControlUpgradeable {
require(hasRole(GAME_ADMIN, msg.sender));
}

function initialize(
address gameContract
) public initializer {
function initialize(address gameContract) public initializer {
__AccessControl_init_unchained();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);

Expand All @@ -39,25 +37,73 @@ contract TokensManager is Initializable, AccessControlUpgradeable {
offsetSlippage = 5;
}

receive() external payable restricted {
}
receive() external payable restricted {}

function teamFight(
uint256[] calldata char,
uint32[] calldata target,
uint8[] calldata fightMultiplier
) external payable {
uint256 totalTokens;
uint256 totalExpectedTokens;
for (uint256 i = 0; i < char.length; i++) {
NindoK marked this conversation as resolved.
Show resolved Hide resolved
(uint256 tokens, uint256 expectedTokens) = game.fight(
msg.sender,
char[i],
target[i],
fightMultiplier[i]
);
totalTokens += tokens;
totalExpectedTokens += expectedTokens;
}
//The following can be put outside the loop? I'm not 100% sure this is correct
NindoK marked this conversation as resolved.
Show resolved Hide resolved
uint256 offset = ABDKMath64x64.mulu(
getSkillToNativeRatio(),
totalExpectedTokens.mul(combatTokenChargePercent).div(100)
);

function fight(uint256 char, uint32 target, uint8 fightMultiplier) external payable {
(uint256 tokens, uint256 expectedTokens) = game.fight(msg.sender, char, target, fightMultiplier);
require(
msg.value >= offset.mul(100 - offsetSlippage).div(100) &&
msg.value <= offset.mul(100 + offsetSlippage).div(100),
"Offset error"
);
if (totalTokens == 0) {
payable(msg.sender).transfer(msg.value);
}
}

uint256 offset = ABDKMath64x64.mulu(getSkillToNativeRatio(), expectedTokens.mul(combatTokenChargePercent).div(100));
function fight(
uint256 char,
uint32 target,
uint8 fightMultiplier
) external payable {
(uint256 tokens, uint256 expectedTokens) = game.fight(
msg.sender,
char,
target,
fightMultiplier
);

uint256 offset = ABDKMath64x64.mulu(
getSkillToNativeRatio(),
expectedTokens.mul(combatTokenChargePercent).div(100)
);

require(
msg.value >= offset.mul(100 - offsetSlippage).div(100) && msg.value <= offset.mul(100 + offsetSlippage).div(100),
'Offset error'
);
msg.value >= offset.mul(100 - offsetSlippage).div(100) &&
msg.value <= offset.mul(100 + offsetSlippage).div(100),
"Offset error"
);

if (tokens == 0) {
payable(msg.sender).transfer(msg.value);
}
}

function retrieve(address addressToTransferTo, uint256 amount) external restricted {
function retrieve(
address addressToTransferTo,
uint256 amount
) external restricted {
payable(addressToTransferTo).transfer(amount);
}

Expand All @@ -80,4 +126,4 @@ contract TokensManager is Initializable, AccessControlUpgradeable {
function setOffsetSlippage(uint8 slippage) external restricted {
offsetSlippage = slippage;
}
}
}
138 changes: 122 additions & 16 deletions frontend/src/store/combat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,13 @@ const combat = {
if (!TokensManager || !CryptoBlades || !rootState.defaultAccount) return;

const res = await TokensManager.methods
.fight(
characterId,
targetString,
fightMultiplier
)
.send({ from: rootState.defaultAccount, gasPrice: getGasPrice(), gas: '300000', value: +offsetCost * fightMultiplier });
.fight(characterId, targetString, fightMultiplier)
.send({
from: rootState.defaultAccount,
gasPrice: getGasPrice(),
gas: '300000',
value: +offsetCost * fightMultiplier,
});

let playerRoll = '';
let enemyRoll = '';
Expand All @@ -167,28 +168,133 @@ const combat = {
toBlock: res.blockNumber,
fromBlock: res.blockNumber
});

if (fightOutcomeEvents.length) {
playerRoll = fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues.playerRoll;
enemyRoll = fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues.enemyRoll;
xpGain = fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues.xpGain;
skillGain = fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues.skillGain;
playerRoll =
fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues
.playerRoll;
enemyRoll =
fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues
.enemyRoll;
xpGain =
fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues.xpGain;
skillGain =
fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues
.skillGain;
}

const {gasPrice} = await rootState.web3.eth.getTransaction(res.transactionHash);
const { gasPrice } = await rootState.web3.eth.getTransaction(
res.transactionHash
);

const bnbGasUsed = gasUsedToBnb(res.gasUsed, gasPrice);
await Promise.all([
dispatch('combat/fetchTargets', {characterId}),
]);
await Promise.all([dispatch('combat/fetchTargets', { characterId })]);

return {
isVictory: parseInt(playerRoll, 10) >= parseInt(enemyRoll, 10),
playerRoll,
enemyRoll,
xpGain,
skillGain,
bnbGasUsed,
};
},

async doEncountersPayNative(
{ rootState, dispatch }: { rootState: IState; dispatch: Dispatch },
{
charactersId,
targetsString,
fightMultiplier,
offsetCost,
}: {
charactersId: number[];
targetsString: number[];
fightMultiplier: number[];
offsetCost: BigNumber;
}
) {
const { TokensManager, CryptoBlades } = rootState.contracts();
if (!TokensManager || !CryptoBlades || !rootState.defaultAccount) return;
const multiplier: string[] = [];
const characters: string[] = [];
const targets: string[] = [];
let totalOffset: BigNumber = offsetCost;
for (let i = 0; i < targetsString.length; i++) {
characters.push(charactersId[i].toString());
targets.push(targetsString[i].toString());
multiplier.push(fightMultiplier[i].toString());
totalOffset = totalOffset.multipliedBy(fightMultiplier[i]);
}

const res = await TokensManager.methods
.teamFight(characters, targets, multiplier)
.send({
from: rootState.defaultAccount,
gasPrice: getGasPrice(),
gas: '800000',
//TODO this should have all the fightMultipliers
value: +offsetCost * fightMultiplier[0],
});

let playerRoll = '';
let enemyRoll = '';
let xpGain = 0;
let skillGain = 0;

for (let i = 0; i < characters.length; i++) {
const fightOutcomeEvents = await CryptoBlades.getPastEvents(
'FightOutcome',
{
filter: {
owner: rootState.defaultAccount!,
character: charactersId[i],
},
toBlock: res.blockNumber,
fromBlock: res.blockNumber,
}
);
if (fightOutcomeEvents.length) {
playerRoll +=
fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues
.playerRoll;
enemyRoll +=
fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues
.enemyRoll;

xpGain += parseInt(
fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues
.xpGain, 10
);

skillGain += parseInt(
fightOutcomeEvents[fightOutcomeEvents.length - 1].returnValues
.skillGain, 10
);
}

if (i < charactersId.length - 1) {
playerRoll += ', ';
enemyRoll += ', ';
}
}

const { gasPrice } = await rootState.web3.eth.getTransaction(
res.transactionHash
);

const bnbGasUsed = gasUsedToBnb(res.gasUsed, gasPrice);

for (let i = 0; i < charactersId.length; i++) {
await dispatch('combat/fetchTargets', { characterId: charactersId[i] });
}

return {
isVictory: parseInt(playerRoll, 10) >= parseInt(enemyRoll, 10),
playerRoll,
enemyRoll,
xpGain,
skillGain,
bnbGasUsed
bnbGasUsed,
};
},

Expand Down
Loading