Skip to content

Commit

Permalink
Merge pull request #1906 from NindoK/Aggregated-fights-into-single-tr…
Browse files Browse the repository at this point in the history
…ansaction

Solving issue #1905: Aggregate Fights into a single txn to reduce gas…
  • Loading branch information
jeffyzxc authored May 11, 2023
2 parents 5569779 + 355ff79 commit 7525766
Show file tree
Hide file tree
Showing 4 changed files with 298 additions and 52 deletions.
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++) {
(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
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

0 comments on commit 7525766

Please sign in to comment.